Authenticate using Azure AD but Authorize using aspnetmembership roles and claims - c#

I am trying to build an ASP.NET MVC 5 application that uses Azure AD for authentication. But once the user is authenticated, I need to use aspnet_membership Microsoft Identity set up to grab the claims for that authenticated logged in user. We don't want to maintain roles and claims within the Azure AD setup and we don't want to use MS Graph.
I have created one MVC 5.0 project using Individual User Accounts in VS 2017, which in turn created the aspnet_membership database in my SQL Server database for me.
I have also created a separate MVC 5.0 project and registered the app in Azure AD and I have the ClientID etc. and that project is also working fine. Now I am trying to merge the two and I am kind of getting lost and doubting if I am thinking it right.
Basically once the user logs in on that Microsoft Azure AD login page, I redirect to a local registration page where when the user registers with just bare minimum info including some roles, and I would then make an entry in the AspNetUsers/Claims tables and I have to attach those claims to the Principal. On subsequent logins for that user, I have to load the clams once authenticated.
Can you please help me in pointing to any samples for this kind of a scenario, as most of what I have read here advice to use Microsoft Graph. But our roles are way too complicated and we have decided to use the local identity aspnet_membership database only for authorization (Roles as Claims).
Thanks

This worked for me.
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string authority = string.Format(System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
var cookieExpiryHours = Int32.Parse(System.Configuration.ConfigurationManager.AppSettings["CookieExpiryHours"]);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager(),
ExpireTimeSpan = TimeSpan.FromMinutes(cookieExpiryHours),
SlidingExpiration=true,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
UseTokenLifetime = false,
// ResponseType is set to request the code id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
SecurityTokenValidated = OnSecurityTokenValidated,
}
}
);
}
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
var OIDClaimDesc = "http://schemas.microsoft.com/identity/claims/objectidentifier";
var claims = context.AuthenticationTicket.Identity.Claims;
var cookieExpiryHours = Int32.Parse(System.Configuration.ConfigurationManager.AppSettings["CookieExpiryHours"]);
context.AuthenticationTicket.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddHours(cookieExpiryHours);
context.AuthenticationTicket.Properties.IsPersistent = false;
var owinContext = context.OwinContext;
var userIdentity = context.AuthenticationTicket.Identity;
var userClaims = userIdentity as ClaimsIdentity;
var firstName = userClaims?.FindFirst(ClaimTypes.GivenName)?.Value ?? string.Empty;
var lastName = userClaims?.FindFirst(ClaimTypes.Surname)?.Value ?? string.Empty;
var email = userClaims?.FindFirst(ClaimTypes.Email)?.Value ?? string.Empty;
var objID = Guid.Parse(userClaims.FindFirst(OIDClaimDesc).Value);
var user = new UserService().GetUser(objID, email);
if (user is null)//This user has just wandered in to the site or the admins have not added this user in the DB yet. Just redirect them back to log out
{
owinContext.Authentication.Challenge();
return Task.FromResult(0);
}
if (userIdentity.IsAuthenticated)
{
userIdentity.AddClaim(new Claim(ClaimTypes.GivenName, firstName));
userIdentity.AddClaim(new Claim(ClaimTypes.Surname, lastName));
userIdentity.AddClaim(new Claim(ClaimTypes.Email, email));
userIdentity.AddClaim(new Claim("AzureID", objID.ToString()));
userIdentity.AddClaim(new Claim(ClaimTypes.Role, "user"));
}
new UserService().UpdateUser(objID, firstName, lastName, email);
foreach (var claim in user.UserClaims)
{
if (!claim.ClaimType.Equals(ClaimTypes.GivenName, StringComparison.OrdinalIgnoreCase)
&& !claim.ClaimType.Equals(ClaimTypes.Surname, StringComparison.OrdinalIgnoreCase))
{
userIdentity.AddClaim(new Claim(ClaimTypes.Role, claim.ClaimValue));
}
}
return Task.FromResult(0);
}
/// <summary>
/// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
var code = "IDX21323";
if (context.Exception.Message.Contains(code)) //I need this, as under certain conditions, the auth process was going on an infinite loop.
{
context.HandleResponse();
context.OwinContext.Authentication.Challenge();
}
return Task.FromResult(true);
}
}
public UserViewModel GetUser(Guid guid, string email)
{
var model = new UserViewModel();
using (var ctxt = new DBContext())
{
var user = ctxt.Users.Where(x => (x.Email == email || x.OID==guid) && x.IsActive).FirstOrDefault();
if (user == null)
return null;
var claims = ctxt.UserClaims.Where(x => x.UserId==user.ID).ToList();
model = Mapper.Map<UserViewModel>(user);
model.UserClaims = Mapper.Map<List<ViewModels.UserClaimViewModel>>(claims);
}
return model;
}

Related

Check SecurityStamp every request using Access Token asp.net identiy

I'm using asp.net identiy to protect my api,
I use the following function to create Access Tokenfor users when they log in
private string GenerateAccessToken(string userName, string role)
{
ClaimsIdentity oAuthIdentity = new ClaimsIdentity(Startup.OAuthOptions.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, userName));
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
DateTime currentUtc = DateTime.UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromDays(365));
string accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
Request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
return accessToken;
}
Everything is fine until I perform an account password update, after that I update SecurityStamp
UserManager.UpdateSecurityStampAsync(loggedinUser.Id);
but the problem is that the token can still be used to call my api without any problem. So how do I check SecurityStamp with each request?
You can check SecurityStamp using the JwtBearerEvents configured in startup.cs or program.cs depending of your .Net version.
This is a very simple version of SecurityStamp validation (.Net 6):
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(cfg =>
{
cfg.Events = new JwtBearerEvents
{
OnTokenValidated = async (ctx) =>
{
var signInManager = ctx.HttpContext.RequestServices
.GetRequiredService<SignInManager<ApplicationUser>>();
var user = await signInManager.ValidateSecurityStampAsync(ctx.Principal);
if (user == null)
{
ctx.Fail("Invalid Security Stamp");
}
}
};
// more code...
});
Note:
For this example to work correctly, you need to ensure that you are packaging the SecurityStamp along with the user's claims during token creation, as in the example below.
var identityOptions = _config.Get<ClaimsIdentityOptions>();
claims.Add(new Claim(identityOptions.SecurityStampClaimType, user.SecurityStamp));

Persisting new claims across requests

How do I add the new claims in such a way that they persist through requests until the cookie expires?
I am using OWIN middle ware, on-premises authentication to authenticate the users logging into the system.
The sign-in part is successful, and I added Roles to the user claims provided by the ws-federation to help authorize the user for certain action methods.
At the time of login, in the controller, I have written the following to add the roles:
string[] roles = { "Role1", "Role2" };
var identity = new ClaimsIdentity(User.Identity);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant
(new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true });
But when I check the claims at the next request, I don't see the role claims.
After successful authentication I believe you added custom claims (normally to some event handler once successfully authenticated). Now in order to persist that information in subsequent request you need to use CookieAuthentication middle ware before your authentication owin in pipeline.
How it works :
Upon successful authentication first time and addition of custom claims, claims will be transformed into sort of auth cookie and sent back to client. Subsequent request will carry this auth cookie. CookieAuthentication middle ware on finding auth cookie will set your Thread.CurrentPriciple with claims obtained from cookie.
During first time request when cookie middle ware does see any auth cookie, it passes request to next middle ware in pipe line (Authentication owin in your case) to challenge user for login.
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies",
AuthenticationMode= AuthenticationMode.Active,
CookieName="XXXXX",
CookieDomain= _cookiedomain,
/* you can go with default cookie encryption also */
TicketDataFormat = new TicketDataFormat(_x509DataProtector),
SlidingExpiration = true,
CookieSecure = CookieSecureOption.Always,
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientID,
Authority = _authority,
RedirectUri = _redirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = SecurityTokenValidated,
AuthenticationFailed = (context) =>
{
/* your logic to handle failure*/
}
},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidIssuers = _validIssuers,
ValidateIssuer = _isValidIssuers,
}
});
EDIT: (Additional information)
Pretty much the exact code as above works for ws federation also, with the same logic and everything.
SecurityTokenValidated = notification =>
{
ClaimsIdentity identity = notification.AuthenticationTicket.Identity;
string[] roles = { "Role1", "Role2" };
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
return Task.FromResult(0);
}
You need to use the same AuthenticationType that you used in Startup.ConfigureAuth. For example:
In Startup.ConfigureAuth:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//....
});
And in your login code (provided in the question):
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
Or make sure that the User.Identity has the same AuthenticationType, and you're good to use that like you did:
var identity = new ClaimsIdentity(User.Identity);
Now the important part is that for the login, you should add the claims before singing the use in, not after. Something like this:
HttpContext.GetOwinContext().Authentication.SignIn(identity);
You can add the claims after signing in, but you will be modifying the cookie right after it is created, which is not efficient. If in some other code you need to modify the claims, then you can use something similar to your code, but you must get the context from Current:
HttpContext.Current.GetOwinContext().Authentication.AuthenticationResponseGrant =
new AuthenticationResponseGrant(new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true });
So you can fix your code by simply adding Current like above, but that's not efficient for the login code and it is better to pass the claims to the SignIn function.
you can do the following in WEB API C # (SOAP),(STORED PROCEDURES)
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
LoginModel model = new LoginModel();
//validate user credentials and obtain user roles (return List Roles)
//validar las credenciales de usuario y obtener roles de usuario
var user = model.User = _serviceUsuario.ObtenerUsuario(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "El nombre de usuario o la contraseƱa no son correctos.cod 01");
return;
}
var stringRoles = user.Roles.Replace(" ", "");//It depends on how you bring them from your DB
string[] roles = stringRoles.Split(',');//It depends on how you bring them from your DB
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
foreach(var Rol in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, Rol));
}
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Correo));
identity.AddClaim(new Claim(ClaimTypes.MobilePhone, user.Celular));
identity.AddClaim(new Claim("FullName", user.FullName));//new ClaimTypes
identity.AddClaim(new Claim("Empresa", user.Empresa));//new ClaimTypes
identity.AddClaim(new Claim("ConnectionStringsName", user.ConnectionStringsName));//new ClaimTypes
//add user information for the client
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
{ "userName",user.NombreUsuario },
{ "FullName",user.FullName },
{ "EmpresaName",user.Empresa }
});
//end
var ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
}

How to get user id from IdentityServer in client app when including access token?

I have implemented an authentication service based on IdentityServer3 and a simple MVC client app and a Shopper API secured by the authentication service. I've implemented a IdentityServer custom UserService so that the authentication service authenticates against our existing user data store. My Shopper API expects a userid in the Shopper Get request. Currently the response from authentication service includes the identity token and the access token, but no user id. I tried adding a user_id claim in the AuthenticationResult from my custom UserService.AuthenticateLocalAsync method, but I'm not seeing it in my client app code.
UserService.AuthenticateLocalAsync looks like this:
try
{
var user = new shopper(_dbConnLib, context.UserName, context.Password);
var claims = new List<Claim> { new Claim("user_id", user.shopperid) };
context.AuthenticateResult = new AuthenticateResult(user.shopperid, user.MemberDetail.billToAddress.FirstName, claims);
}
catch(shopperInitFromException ex)
{
context.AuthenticateResult = null; // Indicates username/password failure
}
return Task.FromResult(0);
And my client app SecurityTokenValidated handler looks like this:
SecurityTokenValidated = async n =>
{
var nid = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role);
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo").ToString());
var userInfo = await userInfoClient.GetAsync(n.ProtocolMessage.AccessToken);
userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Type, ui.Value)));
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
//nid.AddClaim(new Claim("user_id", n.ProtocolMessage.UserId));
nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
}
If I step through that in the debugger, userInfo.Claims always has a count of 0. How can I get back a claim with the unique identifier of the user? Or can I get it from the identity or access token? Or should I just pass the tokens to the Shopper API and let it determine the id from the tokens?
I think I may have the answer. So far, as far as I can tell, the claims I include in the AuthenticateResult constructor in my override of AuthenticateLocalAsync don't seem to go anywhere. But the claims I include in my override of GetProfileDataAsync appear in the token. My GetProfileDataAsync code, which appears to set the claims properly, looks like this:
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = new shopper(_dbConnLib, context.Subject.FindFirst("sub").Value);
var claims = new List<Claim> { new Claim("sub", user.shopperid), new Claim("acr_level", "level 0"), new Claim("amr", "anonymous") };
context.IssuedClaims = claims;
return Task.FromResult(0);
}
My AuthenticateLocalAsync code that sets claims in the AuthenticateResult that I never see in my client app code looks like this:
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
// TODO: Handle AddshopperToBasketException in UserService.AuthenticateLocalAsync
try
{
var user = new shopper(_dbConnLib, context.UserName, context.Password);
var claims = new List<Claim> { new Claim("acr_level", "level 0"), new Claim("amr", "anonymous") };
context.AuthenticateResult = new AuthenticateResult(user.shopperid, user.MemberDetail.billToAddress.FirstName, claims);
}
catch(shopperInitFromException ex)
{
context.AuthenticateResult = null; // Indicates username/password failure
}
return Task.FromResult(0);
}

How to customize oAuth token providers in asp.net web api

I want to change the way the default bearer token system works.
I want to login to the webAPI providing the username, password, and mac address of the device. Like so.
Content-Type: application/x-www-form-urlencoded
username=test&password=P#ssword&grant_type=password&client_id=android&device_info=MAC_Address
I then want the API to provide me with a Refresh Token. This token will be valid for say 7 days and will allow for me to get a new access token. However in the refresh token I want to save / embed the security stamp of the users password in the token along with the extirpation date. This way I can check the security stamp when a new access token is requested. (solves password changing scenario)
My access token only needs to store the bare amount of information for it to work. I don't require that the access token store anything specific. I would like to keep it as small as possible. When it expires I will simply request a new access token using my refresh token.
Now I have tried to implement the above but have got my self heavily confused about what to implement where. Here's what i have got.
Step 1: The Startup.Auth.cs
//Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20)
};
Now in here I already have some questions. I want to have two providers, one which handles Refresh Tokens and one that handles Access Tokens. Which providers do I need to set? because I see there is also one called AccessTokenProvider = then what is Provider = for?
Step 2: The RereshTokenProvider. This is what I have so far:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
//Used to store all the refresh tokens
public static ConcurrentDictionary<string, AuthenticationTicket> RefreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString("N");
//copy properties and set the desired lifetime of refresh token
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
};
//TODO: get mac address from the request headers??
//TODO: save the mac address to db along with user and date
var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
RefreshTokens.TryAdd(guid, refreshTokenTicket);
context.SetToken(guid);
return Task.FromResult<object>(null);
}
public Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (RefreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
return Task.FromResult<object>(null);
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
}
Now if i understand correctly. The purpose of the SimpleRefreshTokenProvider is to build up a RefreshToken and to the validate it when the api receives a request with one in it?
Step 3: SimpleAuthorizationServerProvider. This is what I have so far. but I have a feeling this is where I have gone wrong. Or im getting confused, What is the purpose of this class? Is it not to validate the AccessToken?
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Not concerned about clients yet
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// validate user credentials
var userManager = context.OwinContext.GetUserManager<FskUserManager>();
FskUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
// create identity
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
//Set properties of the token
// create metadata to pass on to refresh token provider
AuthenticationProperties properties = new AuthenticationProperties(new Dictionary<string, string>
{
{"userName", user.UserName}
});
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
string originalClient;
context.Ticket.Properties.Dictionary.TryGetValue("as:client_id", out originalClient);
var currentClient = context.ClientId;
// chance to change authentication ticket for refresh token requests
var newId = new ClaimsIdentity(context.Ticket.Identity);
newId.AddClaim(new Claim("newClaim", "refreshToken"));
var newTicket = new AuthenticationTicket(newId, context.Ticket.Properties);
context.Validated(newTicket);
}
}
Please what am I missing here?

ASP.NET Identity and Claim-based

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.

Categories