in my current application I am using Owin + Aspnet Identity along with Microsoft Live OAuth provider to handle authentication.
So far everything works fine except for my attempts to retrieve the remote token, in order to store it in my database.
I have found some documentation online which says to enable "saveBootstrapContext" in the web.config, and so I did:
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
<securityTokenHandlers>
<securityTokenHandlerConfiguration saveBootstrapContext="true"></securityTokenHandlerConfiguration>
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
I tried only on identityConfiguration then only on securityTokenHandlerConfiguration and then both together, but the result is always the same. In the following code externalData.ExternalIdentity.BootstrapContext is always null.
The SignIn method gets called inside the "ExternalLoginCallback" method which is called by the middleware.
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Web;
// custom namespaces redacted
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
public class AuthManager : IAuthManager
{
private readonly IUserBusinessLogic userBusinessLogic;
public AuthManager(IUserBusinessLogic userBusinessLogic)
{
this.userBusinessLogic = userBusinessLogic;
}
public void SignIn()
{
IAuthenticationManager manager = HttpContext.Current.GetOwinContext().Authentication;
var externalData = manager.GetExternalLoginInfo();
UserDto user = this.userBusinessLogic.GetUser(externalData.Login.LoginProvider, externalData.Login.ProviderKey);
var token = ((BootstrapContext)externalData.ExternalIdentity.BootstrapContext).Token;
if (user == null)
{
user = this.userBusinessLogic.AddUser(new UserDto(), externalData.Login.LoginProvider, externalData.Login.ProviderKey, token);
}
user.Token = token;
var claims = new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()),
new Claim(ClaimTypes.UserData, UserData.FromUserDto(user).ToString())
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var properties = new AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true
};
manager.SignIn(properties, identity);
}
Some other posts here on SO said to try to restart IIS, restart the machine, empty the browser cookies and restart the browser. I tried all of that and still nothing. If I mock the token string everything else works properly.
Now I am clearly missing something but I can't find any clear documentation online.
Any help is really appreciated.
Thanks.
Sometimes no help is the best help, as I've been forced to dig deeper and deeper, ultimately to find the solution.
Due premise is I was in total confusion and I was mixing three different technologies without understanding all implications.
My example used WIF configuration in web.config but then code side it was using Aspnet Identity atop of OWIN (which doesn't use web.config at all).
Once I got my ideas straight, I realized the following:
WIF was totally unneeded, therefore I got rid of all that configuration (and of WIF altogether)
Since my MS auth was being performed by the specific OWIN Middleware that handles it, I had to understand how to configure it to retrieve the token
Aspnet Identity was being used only for the DefaultAuthenticationTypes static class, which provides some string constants. I kept it for simplicity sake but I could as well remove it.
So my refactored (and working) code looks like this. First of all, the Middleware configuration needed to get MS auth working along with the token, inside Startup.cs
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions
{
ClientId = "myClientId",
ClientSecret = "myClientSecret",
Provider = new MicrosoftAccountAuthenticationProvider
{
OnAuthenticated = context =>
{
// here's the token
context.Identity.AddClaim(new System.Security.Claims.Claim("AccessToken", context.AccessToken));
context.Identity.AddClaim(new System.Security.Claims.Claim("FirstName", context.FirstName));
context.Identity.AddClaim(new System.Security.Claims.Claim("LastName", context.LastName));
return Task.FromResult(true);
}
}
});
Then the revisited SignIn method:
public void SignIn()
{
IAuthenticationManager manager = HttpContext.Current.GetOwinContext().Authentication;
var externalData = manager.GetExternalLoginInfo();
UserDto user = this.userBusinessLogic.GetUser(externalData.Login.LoginProvider, externalData.Login.ProviderKey);
if (user == null)
{
user = this.userBusinessLogic.AddUser(
new UserDto
{
FirstName = externalData.ExternalIdentity.Claims.Single(c => c.Type == "FirstName").Value,
LastName = externalData.ExternalIdentity.Claims.Single(c => c.Type == "LastName").Value
},
externalData.Login.LoginProvider,
externalData.Login.ProviderKey,
// here's the token claim that I set in the middleware configuration
externalData.ExternalIdentity.Claims.Single(c => c.Type == "AccessToken").Value);
}
var claims = new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()),
new Claim(ClaimTypes.UserData, UserData.FromUserDto(user).ToString()),
new Claim("AccessToken", user.Token),
new Claim("FirstName", user.FirstName),
new Claim("LastName", user.LastName)
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var properties = new AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true
};
manager.SignIn(properties, identity);
}
Maybe this was difficult just to me, but anyway here I am posting my solution hoping it can save some headaches and some days of swearing to some fellow developer.
Happy coding ^^
Related
I use an external provider in order to authenticate user in my ASP.NET MVC app without any problem. However, I also need to authorize users in order to prevent them from direct access or expired access (session for 2 min). I had used ASP.NET Identity before, but this time I do not need to keep neither users nor roles on the table and for this reason I need a quick and good workaround for this problem. So, how can I prevent a user accessing the In dex page of my app without authenticating by the provider that I use. Similarly I also need to check if there is more than 2 minutes after user's last action and in such sitıuation I need to redirect user to Login page. I tried to use OWIN Cookie, but unfortunately I cannot logout user by using at least 10 different approach :(
Startup:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
//other options
ExpireTimeSpan = TimeSpan.FromMinutes(1)
//Provider = new CookieAuthenticationProvider(),
//CookieName = "MyCookieName",
//CookieHttpOnly = true
});
}
}
Controller:
[HttpGet]
public ActionResult Login(string code)
{
//At this stage I want to force user to sign out, but none of the following methods work
//method 1
HttpContext.GetOwinContext().Authentication.SignOut("ApplicationCookie");
//method 2
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignOut("ApplicationCookie");
//or
//authManager.SignOut();
//method 3
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
//method 4 (using only one of them at a time)
Request.GetOwinContext().Authentication.SignOut();
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
//check session
var isAuthenticated = HttpContext.GetOwinContext().Authentication.User.Identity.IsAuthenticated; // >>> always returns true
string tc = HttpContext.GetOwinContext().Authentication.User.Identity.Name; // >>> always returns name value
//if user is authenticated via OAuth2.0
if (user.isAuthenticated)
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name)
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
//// Add roles into claims
//var roles = _roleService.GetByUserId(user.Id);
//if (roles.Any())
//{
// var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
// identity.AddClaims(roleClaims);
//}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties
{ IsPersistent = false }, identity); // ??? I am not sure if IsPersistent should be true ?
return View();
}
// login failed
return RedirectToAction("Account", "Login");
}
Finally I have fixed the problem by using OWIN cookie authentication. Here is the code for those who might need to use OWIN cookie authentication on ASP.NET MVC.
On the other hand, I would really like to integrate JWT to my ASP.NET MVC project, but unfortunately was not able to do. However, many thanks and voted up the answers that are also helpful for me.
Startup:
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
LogoutPath = new PathString("/Account/LogOff"),
ExpireTimeSpan = TimeSpan.FromMinutes(5),
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider(),
CookieName = "YOUR_COOKIE_NAME",
CookieHttpOnly = true,
// !!! Using this setting "Always" causing "302 Redirect..." error while ddebugging >>>
CookieSecure = CookieSecureOption.SameAsRequest
});
}
AccountController:
public ActionResult Login()
{
//authenticate user
var user = db.GetUser("John");
if (user != null)
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.Email)
//you can add more claims
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
// Add roles into claims
var roles = _roleService.GetByUserId(user.Id);
if (roles.Any())
{
var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
identity.AddClaims(roleClaims);
}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties
{ IsPersistent = true }, identity);
return RedirectToAction("Index", "Home");
}
// login failed.
return RedirectToAction("Login", "Account");
}
You need to set the [Authorize] attribute on the action and/or controller.
And about the session only last for 2 minutes. You can put the timestamp in a session cookie when the user logins and then build a middleware to check the session value each time an action is made. If the session value is older than 2 minutes log out the user.
How to use session:
In Startup file add:
services.AddSession(options =>
{
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
and
app.UseSession();
//middleware for checking the 2 minute limit
app.UseMiddleware<SignoutMiddleware>();
Add the session wherever your user gets logged in:
HttpContext.Session.SetString(subjectId, DateTime.Now);
Middleware:
public class SignoutMiddleware
{
private readonly RequestDelegate _next;
public SignoutMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var sessionExpire = context.Session.GetString(context.User.GetSubjectId());
//Do some logic here
await _next.Invoke(context);
}
}
As for what your code currently does for login you probably needs to change some. But there should be many tutorials if you just google some :)
You could use a Entity Framwork, JSON Web Token (JWT) and Claims. It is really easy to limit the amount of time (days, hours, minutes) you want a user to have access to a section of your Controllers with JWTs.
You can limit the amount of time a JWT has access for by using the Expires object in SecurityTokenDescriptor. So in your case I would do the following:
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
There is several great FULL examples done by Jason Watmore for .NET Core with Role Based Auth and Secure Auth For Hashed Password in Database. Not sure what library you're using though so if this does not help you it would help me to help you if you specify.
I'm AmirReza that you talking before.
Edit
In order for MVC to understand anything about your JWT you basically have to tell it :-) . First, install the Jwt package from nuget:
Install-Package Microsoft.Owin.Security.Jwt
Then open up your Startup.cs file and add a new funtion that will tell MVC how to consume JWT. At basics your Startup will look something like:
using System.Configuration;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Owin;
[assembly: OwinStartupAttribute(typeof(TOMS.Frontend.Startup))]
namespace TOMS.Frontend {
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["Issuer"];
var audienceId = ConfigurationManager.AppSettings["AudienceId"];
var audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
You will notice that I am placing the issuer, audienceId and audienceSecret in my Web.config file. (Those values should match the ones on your Resource Server). Also, you might want to ensure you have an updated System.IdentityModel.Tokens.Jwt running:
Update-package System.IdentityModel.Tokens.Jwt
With those set, you may decorate your controller Action with the [Authorize] attribute and play ball.
UPDATE By The way, if you wish to add the values in your web.config file in order to retrieve them as I did above; simply add them under the AppSettings:
<configuration> <appSettings> <add key="Issuer" value="YOUR_ISSUER" /> <add key="AudienceId" value="YOUR_AUDIENCEID" /> <add key="AudienceSecret" value="YOUR_AUDIENCESECRET" /> </appSettings> </configuration>
My setup,
An IdentityServer using MVC Identity to store the Users, created with dotnet new mvc -au Individual and applying the http://docs.identityserver.io/en/release/quickstarts/0_overview.html tutorial, running in localhost 5000.
A client App, but now I'm using postman to do tests.
A WEB API, created with dotnet new webapi, running in localhost 5001.
The IdentityServer resources and clients configuration is the following, notice that I'm using reference tokens:
public static IEnumerable<IdentityResource> GetIdentityResources() {
return new List<IdentityResource>{ new IdentityResources.OpenId() };
}
public static IEnumerable<ApiResource> GetApiResources() {
return new List<ApiResource>{
new ApiResource("api_resource", "API Resource") {
Description= "API Resource Access",
ApiSecrets= new List<Secret> { new Secret("apiSecret".Sha256()) },
}
};
}
public static IEnumerable<Client> GetClients() {
return new List<Client>{
new Client {
ClientId= "angular-client",
ClientSecrets= { new Secret("secret".Sha256()) },
AllowedGrantTypes= GrantTypes.ResourceOwnerPassword,
AllowOfflineAccess= true,
AccessTokenType = AccessTokenType.Reference,
AlwaysIncludeUserClaimsInIdToken= true,
AllowedScopes= { "api_resource" }
}
}
The password and user is send with postman and the token received is send to the WEB API also with postman, something like call localhost:5001/v1/test with the token pasted in option bearer token.
In the API Startup, in ConfigureServices I'm adding the lines below
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority= "http://localhost:5000";
options.ApiName= "api_resource";
options.ApiSecret = "apiSecret";
});
And I'm getting the Id of the user inside the controller as follows:
public async Task<IActionResult> Get(int id) {
var discoveryClient = new DiscoveryClient("http://localhost:5000");
var doc = await discoveryClient.GetAsync();
var introspectionClient = new IntrospectionClient(
doc.IntrospectionEndpoint,
"api_resource",
"apiSecret");
var token= await HttpContext.GetTokenAsync("access_token");
var response = await introspectionClient.SendAsync(
new IntrospectionRequest { Token = token });
var userId = response.Claims.Single(c => c.Type == "sub").Value;
}
The question itself is, am I using the right path to get the Id from the reference token?, because now It works but I don't want to miss anything, specially thinking that is a security concern.
I'm asking also because I have seen anothers using
string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
that is more straightforward but doesn't seems to fit with reference tokens.
Thanks in advance.
Inside a controller action that is protected with an [Authorize] attribute you can simply get claims directly from the ClaimsPrinciple, without having to go through a manual discovery client. The claims principle is handily aliased simply with User inside your controllers.
I'm asking also because I have seen anothers using
string userId = User.Claims.FirstOrDefault(c => c.Type ==
ClaimTypes.NameIdentifier).Value;
that is more straightforward but doesn't seems to fit with reference
tokens.
It works just fine with reference tokens. You should have no problems accessing the sub claim.
EDIT:
As I mentioned in a comment below, I tend to use the standard JwtClaimTypes and create some extension methods on the ClaimsPrinciple, such as:
public static string GetSub(this ClaimsPrincipal principal)
{
return principal?.FindFirst(x => x.Type.Equals(JwtClaimTypes.Subject))?.Value;
}
or
public static string GetEmail(this ClaimsPrincipal principal)
{
return principal?.FindFirst(x => x.Type.Equals(JwtClaimTypes.Email))?.Value;
}
... so that within my protected actions I can simply use User.GetEmail() to get hold of claim values.
It's worth stating the obvious, that any method for retrieving claim values will only work if the claims actually exist. i.e. asking for the ZoneInfo claim will not work unless that claim was requested as part of the token request in the first place.
I'm making an API for Exact Online for a website with a form. The visitor will fill in his information and after that the visitor sends it. It need to be send to the Exact online account from my client. But before that I need a accesstoken. The problem is that I don't want to give the user the login page that Exact gives me. I'm searching already for days to find a way to skip the login or to enter the login information by backend (there is always 1 login, and that is the login from my client).
Now this authorization thing is something new for me. So far I know I can call my authorization settings from the startup with this:
HttpContext.Authentication.GetAuthenticateInfoAsync("ExactOnline");
But then I get that loginscreen that I don't want. The only thing that Exact is telling me to do:
Create an app registration that supports an automated connection wizard (your provisioning process).
Is there a way to send them the login information and the visitor doesn't see a loginpage.
In my Startup.cs
var s = new OAuthOptions{
AuthenticationScheme = "ExactOnline",
ClientId = "CLIENTID",
ClientSecret = "CLIENTSECRET",
CallbackPath = new PathString("/callback"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthorizationEndpoint = new Uri(string.Format("{0}/api/oauth2/auth", "https://start.exactonline.nl")).ToString(),
TokenEndpoint = new Uri(string.Format("{0}/api/oauth2/token", "https://start.exactonline.nl")).ToString(),
//Scope = { "identity", "roles" },
Events = new OAuthEvents
{
OnCreatingTicket = context =>
{
context.Identity.AddClaim(new Claim("urn:token:exactonline", context.AccessToken));
return Task.FromResult(true);
}
}
};
app.UseOAuthAuthentication(s);
First i had this code, but that gives me a null exception when i put the identity in the claimprincipal, probably because my claimsprincipal is null and i don't know why.
HttpContext.Authentication.AuthenticateAsync("ExactOnline");
var identity = new ClaimsIdentity("ExactOnline",ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.Label = "Authentication";
identity.AddClaim(new Claim(ClaimTypes.Name, "USERNAME?"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "PASSWORD?"));
claimsPrincipal.AddIdentity(identity);
var test = HttpContext.Authentication.SignInAsync("ExactOnline", claimsPrincipal, new AuthenticationProperties() { IsPersistent = false }));
After that i tried following code, but that also didn't work. My code will continue, but the test variable will be filled with this message: The name 'InnerExceptionCount' does not exist in the current context.
var identity = new ClaimsIdentity("ExactOnline", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.Label = "Authentication";
identity.AddClaim(new Claim("username", "USERNAME"));
identity.AddClaim(new Claim("password", "PASSWORD"));
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(identity);
var test = HttpContext.Authentication.SignInAsync("ExactOnline", claimsPrincipal, new AuthenticationProperties() { IsPersistent = false });
Someone know how to solve this problem?
I have a default ASP.NET Core website created within Visual Studio 2017. I have chosen to authenticate using an Azure Active Directory.
I run the site and can successfully login using an account in the Active Directory.
I can retrieve Claim information provided by Active Directory, e.g. by calling the following line I get the 'name'.
User.Claims.FirstOrDefault(c => c.Type == "name")?.Value;
I want to add a custom claim - CompanyId = 123456 for the logged in user.
I'm able to add a custom claim however it is only available on the page where the claim is set.
Claim claim = new Claim("CompanyId", "123456", ClaimValueTypes.String);
((ClaimsIdentity)User.Identity).AddClaim(claim);
My understanding is that I somehow need to update the token that has been issued by Active Directory or set the claim before the token is issued. I'm unsure how to do this.
I suspect this needs to be done in the AccountController at SignIn()
// GET: /Account/SignIn
[HttpGet]
public IActionResult SignIn()
{
return Challenge(
new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
}
I've read numerous articles and samples about this scenario (including https://github.com/ahelland/AADGuide-CodeSamples/tree/master/ClaimsWebApp) however have not managed to solve how to persist the Claim across requests.
I have successfully managed to persist custom Claims using ASP.NET Identity as the Authentication Provider, but this appears to be because the custom Claim is saved to the database..
OnTokenValidated offers you the chance to modify the ClaimsIdentity obtained from the incoming token , code below is for your reference :
private Task TokenValidated(TokenValidatedContext context)
{
Claim claim = new Claim("CompanyId", "123456", ClaimValueTypes.String);
(context.Ticket.Principal.Identity as ClaimsIdentity).AddClaim(claim);
return Task.FromResult(0);
}
Setting the OpenIdConnectEvents:
Events = new OpenIdConnectEvents
{
OnRemoteFailure = OnAuthenticationFailed,
OnAuthorizationCodeReceived = OnAuthorizationCodeReceived,
OnTokenValidated = TokenValidated
}
Then in controller using :
var companyId= User.Claims.FirstOrDefault(c => c.Type == "CompanyId")?.Value;
For those who would like more detail, the code provided is placed in Startup.cs
In the Configure method add/edit:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["Authentication:AzureAd:ClientId"],
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
CallbackPath = Configuration["Authentication:AzureAd:CallbackPath"],
Events = new OpenIdConnectEvents
{
OnTokenValidated = TokenValidated
}
});
The private Task TokenValidated method is in the body of Startup.cs
The following sample is a good reference.
https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore-v2/blob/master/WebApp-OpenIDConnect-DotNet/Startup.cs
How to use claims? For example, I want to set access to each page (resource) for each user. I understand, I can do it using roles, but as I understand, claim-based is more effectively. But when I try to create a claim, I see the following method:
userIdentity.AddClaim(new Claim(ClaimTypes.Role, "test role"));
first parameter of constructor of Claim class get ClaimTypes enum, which has many "strange" members like Email, Phone etc. I want to set that this claim and then check this claim to have access to certain resource. I'm on wrong way? How to do it?
From the code above, I am assuming you have already added the claim in startup class on authenticated of your provider as below.
context.Identity.AddClaim(new Claim("urn:google:name", context.Identity.FindFirstValue(ClaimTypes.Name))); // added claim for reading google name
context.Identity.AddClaim(new Claim("urn:google:email", context.Identity.FindFirstValue(ClaimTypes.Email))); // and email too
Once you have added the claims in startup, when the request is actually processed check if its a callback and if yes, read the claims as below(in IHttpHandler).
public void ProcessRequest(HttpContext context)
{
IAuthenticationManager authManager = context.GetOwinContext().Authentication;
if (string.IsNullOrEmpty(context.Request.QueryString[CallBackKey]))
{
string providerName = context.Request.QueryString["provider"] ?? "Google";//I have multiple providers so checking if its google
RedirectToProvider(context, authManager, providerName);
}
else
{
ExternalLoginCallback(context, authManager);
}
}
If its 1st call redirect to provider
private static void RedirectToProvider(HttpContext context, IAuthenticationManager authManager, string providerName)
{
var loginProviders = authManager.GetExternalAuthenticationTypes();
var LoginProvider = loginProviders.Single(x => x.Caption == providerName);
var properties = new AuthenticationProperties()
{
RedirectUri = String.Format("{0}&{1}=true", context.Request.Url, CallBackKey)
};
//string[] authTypes = { LoginProvider.AuthenticationType, DefaultAuthenticationTypes.ExternalCookie };
authManager.Challenge(properties, LoginProvider.AuthenticationType);
//without this it redirect to forms login page
context.Response.SuppressFormsAuthenticationRedirect = true;
}
And finally read the claims you get back
public void ExternalLoginCallback(HttpContext context, IAuthenticationManager authManager)
{
var loginInfo = authManager.GetExternalLoginInfo();
if (loginInfo == null)
{
throw new System.Security.SecurityException("Failed to login");
}
var LoginProvider = loginInfo.Login.LoginProvider;
var ExternalLoginConfirmation = loginInfo.DefaultUserName;
var externalIdentity = authManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
var pictureClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type.Equals("picture"));
var pictureUrl = pictureClaim.Value;
LogInByEmail(context, email, LoginProvider); //redirects to my method of adding claimed user as logged in, you will use yours.
}
Claim doesn't set permission. It's used to verify you that "you are who you claim to be you are". These claims are identified by issuer, usually a 3rd party. See for example this article for description.
So, you should define which claims are necessary (who user should be) in order to access a certain page. Otherwise, using claim-based authorization will be same as using identity based or role based.