public Task StoreDownstreamIdTokenResponseAsync(string id, DownstreamAuthorizeResponse response) { var key = OIDCPipleLineStoreUtils.GenerateDownstreamIdTokenResponseKey(id); _memoryCache.Set( key, response, TimeSpan.FromMinutes(_options.ExpirationMinutes)); return(Task.CompletedTask); }
public async Task OnGetAsync() { if (User.Identity.IsAuthenticated) { string nonce = _oidcPipeLineKey.GetOIDCPipeLineKey(); IdTokenResponse = await _oidcPipelineStore.GetDownstreamIdTokenResponseAsync(nonce); Claims = Request.HttpContext.User.Claims.ToList(); } }
public async Task StoreDownstreamIdTokenResponseAsync(string id, DownstreamAuthorizeResponse response) { var key = OIDCPipleLineStoreUtils.GenerateDownstreamIdTokenResponseKey(id); var data = _binarySerializer.Serialize(response); DistributedCacheEntryOptions options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_options.ExpirationMinutes) }; await _cache.SetAsync(key, data, options); }
public static IdentityBuilder AddAuthentication <TUser>(this IServiceCollection services, IConfiguration configuration, Action <IdentityOptions> setupAction) where TUser : class { // Services used by identity var authenticationBuilder = services.AddAuthentication(options => { options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; }); // doesn't work here either. //---------------------------------- /* * services.AddOptions<OpenIdConnectOptions>() * .Configure<IOIDCPipelineStore, IHttpContextAccessor>((options, oidcPipelineStore, accessor) => * { * options.ProtocolValidator = new MyOpenIdConnectProtocolValidator(oidcPipelineStore, accessor) * { * RequireTimeStampInNonce = false, * RequireStateValidation = false, * RequireNonce = true, * NonceLifetime = TimeSpan.FromMinutes(15) * }; * }); */ var section = configuration.GetSection("openIdConnect"); var openIdConnectSchemeRecordSchemeRecords = new List <OpenIdConnectSchemeRecord>(); section.Bind(openIdConnectSchemeRecordSchemeRecords); foreach (var record in openIdConnectSchemeRecordSchemeRecords) { var scheme = record.Scheme; services.AddOptions <OpenIdConnectOptions>(scheme) .Configure <IOIDCPipeLineKey, IOIDCPipelineStore>( (options, oidcPipeLineKey, oidcPipelineStore) => { options.ProtocolValidator = new OIDCPipelineOpenIdConnectProtocolValidator(oidcPipeLineKey, oidcPipelineStore) { RequireTimeStampInNonce = false, RequireStateValidation = false, RequireNonce = true, NonceLifetime = TimeSpan.FromMinutes(15) }; }); authenticationBuilder.AddOpenIdConnect(scheme, scheme, options => { options.Authority = record.Authority; options.CallbackPath = record.CallbackPath; options.RequireHttpsMetadata = false; if (!string.IsNullOrEmpty(record.ResponseType)) { options.ResponseType = record.ResponseType; } options.GetClaimsFromUserInfoEndpoint = record.GetClaimsFromUserInfoEndpoint; options.ClientId = record.ClientId; options.ClientSecret = record.ClientSecret; options.SaveTokens = true; options.TokenValidationParameters.ValidateAudience = false; options.Events.OnMessageReceived = async context => { }; options.Events.OnTokenValidated = async context => { var pipeLineStore = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipelineStore>(); OpenIdConnectMessage oidcMessage = null; if (context.Options.ResponseType == "id_token") { oidcMessage = context.ProtocolMessage; } else { oidcMessage = context.TokenEndpointResponse; } var userState = context.ProtocolMessage.Parameters["state"].Split('.')[0]; var header = new JwtHeader(); var handler = new JwtSecurityTokenHandler(); var idToken = handler.ReadJwtToken(oidcMessage.IdToken); var claims = idToken.Claims.ToList(); var stored = await pipeLineStore.GetOriginalIdTokenRequestAsync(userState); DownstreamAuthorizeResponse idTokenResponse = new DownstreamAuthorizeResponse { Request = stored, AccessToken = oidcMessage.AccessToken, ExpiresAt = oidcMessage.ExpiresIn, IdToken = oidcMessage.IdToken, RefreshToken = oidcMessage.RefreshToken, TokenType = oidcMessage.TokenType, LoginProvider = scheme }; await pipeLineStore.StoreDownstreamIdTokenResponseAsync(userState, idTokenResponse); }; options.Events.OnRedirectToIdentityProvider = async context => { var oidcPipelineKey = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipeLineKey>(); string key = oidcPipelineKey.GetOIDCPipeLineKey(); var pipeLineStore = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipelineStore>(); var stored = await pipeLineStore.GetOriginalIdTokenRequestAsync(key); var clientSecretStore = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipelineClientStore>(); if (stored != null) { var clientRecord = await clientSecretStore.FetchClientRecordAsync(scheme, stored.ClientId); context.ProtocolMessage.ClientId = stored.ClientId; context.Options.ClientId = stored.ClientId; context.Options.ClientSecret = clientRecord.Secret; context.ProtocolMessage.State = $"{key}."; } context.Options.Authority = context.Options.Authority; if (record.AdditionalProtocolScopes != null && record.AdditionalProtocolScopes.Any()) { string additionalScopes = ""; foreach (var item in record.AdditionalProtocolScopes) { additionalScopes += $" {item}"; } context.ProtocolMessage.Scope += additionalScopes; } if (context.HttpContext.User.Identity.IsAuthenticated) { // assuming a relogin trigger, so we will make the user relogin on the IDP context.ProtocolMessage.Prompt = "login"; } var allowedParams = await clientSecretStore.FetchAllowedProtocolParamatersAsync(scheme); foreach (var allowedParam in allowedParams) { var item = stored.Raw[allowedParam]; if (item != null) { if (string.Compare(allowedParam, "state", true) == 0) { context.ProtocolMessage.SetParameter(allowedParam, $"{key}.{item}"); } else { context.ProtocolMessage.SetParameter(allowedParam, item); } } } /* * if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication) * { * context.ProtocolMessage.AcrValues = "v1=google"; * } */ }; options.Events.OnRemoteFailure = context => { context.Response.Redirect("/"); context.HandleResponse(); return(Task.CompletedTask); }; }); } return(new IdentityBuilder(typeof(TUser), services)); }
/* * public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) * { * returnUrl = returnUrl ?? Url.Content("~/"); * if (remoteError != null) * { * ErrorMessage = $"Error from external provider: {remoteError}"; * return RedirectToPage("./Login", new {ReturnUrl = returnUrl }); * } * var info = await _signInManager.GetExternalLoginInfoAsync(); * if (info == null) * { * ErrorMessage = "Error loading external login information."; * return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); * } * * // Sign in the user with this external login provider if the user already has a login. * var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); * if (result.Succeeded) * { * _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); * return LocalRedirect(returnUrl); * } * if (result.IsLockedOut) * { * return RedirectToPage("./Lockout"); * } * else * { * // If the user does not have an account, then ask the user to create an account. * ReturnUrl = returnUrl; * LoginProvider = info.LoginProvider; * if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) * { * Input = new InputModel * { * Email = info.Principal.FindFirstValue(ClaimTypes.Email) * }; * } * return Page(); * } * } */ public async Task <IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) { string currentNameIdClaimValue = null; if (User.Identity.IsAuthenticated) { // we will only create a new user if the user here is actually new. var qName = from claim in User.Claims where claim.Type == ".nameIdentifier" select claim; var nc = qName.FirstOrDefault(); currentNameIdClaimValue = nc?.Value; } returnUrl = returnUrl ?? Url.Content("~/"); if (remoteError != null) { ErrorMessage = $"Error from external provider: {remoteError}"; return(RedirectToPage("./Login", new { ReturnUrl = returnUrl })); } var info = await _signInManager.GetExternalLoginInfoAsync(); if (info == null) { ErrorMessage = "Error loading external login information."; return(RedirectToPage("./Login", new { ReturnUrl = returnUrl })); } var oidc = await HarvestOidcDataAsync(); DownstreamAuthorizeResponse idTokenResponse = new DownstreamAuthorizeResponse { AccessToken = oidc["access_token"], ExpiresAt = oidc["expires_at"], IdToken = oidc["id_token"], RefreshToken = oidc["refresh_token"], TokenType = oidc["token_type"], LoginProvider = info.LoginProvider }; // await _oidcPipelineStore.StoreDownstreamIdTokenResponseAsync(idTokenResponse); var queryNameId = from claim in info.Principal.Claims where claim.Type == ClaimTypes.NameIdentifier select claim; var nameIdClaim = queryNameId.FirstOrDefault(); var query = from claim in info.Principal.Claims where _possibleNameTypes.Contains(claim.Type) select claim; var nameClaim = query.FirstOrDefault(); var displayName = nameIdClaim.Value; if (nameClaim != null) { displayName = nameClaim.Value; } if (currentNameIdClaimValue == nameIdClaim.Value) { // this is a re login from the same user, so don't do anything; return(LocalRedirect(returnUrl)); } /* * // Sign in the user with this external login provider if the user already has a login. * var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); * * if (result.Succeeded) * { * // Update the token * await _signInManager.UpdateExternalAuthenticationTokensAsync(info); * * _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); * return LocalRedirect(returnUrl); * } * if (result.IsLockedOut) * { * return RedirectToPage("./Lockout"); * } * else * { * // If the user does not have an account, then ask the user to create an account. * ReturnUrl = returnUrl; * LoginProvider = info.LoginProvider; * if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) * { * Input = new InputModel * { * Email = info.Principal.FindFirstValue(ClaimTypes.Email) * }; * } * return Page(); * } */ var leftoverUser = await _userManager.FindByEmailAsync(displayName); if (leftoverUser != null) { await _userManager.DeleteAsync(leftoverUser); // just using this inMemory userstore as a scratch holding pad } var user = new ApplicationUser { UserName = nameIdClaim.Value, Email = displayName }; var result = await _userManager.CreateAsync(user); if (result.Succeeded) { var newUser = await _userManager.FindByIdAsync(user.Id); var eClaims = new List <Claim> { new Claim("display-name", displayName), new Claim("login_provider", info.LoginProvider) }; // normalized id. await _userManager.AddClaimsAsync(newUser, eClaims); await _signInManager.SignInAsync(user, isPersistent : false); await _userManager.DeleteAsync(user); // just using this inMemory userstore as a scratch holding pad _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); return(LocalRedirect(returnUrl)); } return(RedirectToPage("./Login", new { ReturnUrl = returnUrl })); }
public async Task <IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (remoteError != null) { ErrorMessage = $"Error from external provider: {remoteError}"; return(RedirectToPage("./Login", new { ReturnUrl = returnUrl })); } var info = await _signInManager.GetExternalLoginInfoAsync(); if (info == null) { ErrorMessage = "Error loading external login information."; return(RedirectToPage("./Login", new { ReturnUrl = returnUrl })); } var oidc = await HarvestOidcDataAsync(); DownstreamAuthorizeResponse idTokenResponse = new DownstreamAuthorizeResponse { AccessToken = oidc["access_token"], ExpiresAt = oidc["expires_at"], IdToken = oidc["id_token"], RefreshToken = oidc["refresh_token"], TokenType = oidc["token_type"], LoginProvider = info.LoginProvider }; var externalPrincipalClaims = info.Principal.Claims.ToList(); var nameIdentifier = GetValueFromClaim(externalPrincipalClaims, ClaimTypes.NameIdentifier); await _signInManager.SignOutAsync(); var user = new IdentityUser { UserName = nameIdentifier, Email = null }; var result = await _userManager.CreateAsync(user); if (result.Succeeded) { var newUser = await _userManager.FindByIdAsync(user.Id); var eClaims = new List <Claim> { new Claim(".displayName", user.Id), new Claim(".externalNamedIdentitier", nameIdentifier), new Claim(".loginProvider", info.LoginProvider) }; // normalized id. await _userManager.AddClaimsAsync(newUser, eClaims); await _signInManager.SignInAsync(user, isPersistent : false); await _userManager.DeleteAsync(user); // just using this inMemory userstore as a scratch holding pad return(LocalRedirect(returnUrl)); } return(RedirectToPage("./Login", new { ReturnUrl = returnUrl })); }
private TokenRequestValidationResult ValidateAuthorizationCodeWithProofKeyParameters(string codeVerifier, DownstreamAuthorizeResponse idTokenResopnse) { if (idTokenResopnse.Request.CodeChallenge.IsMissing() || idTokenResopnse.Request.CodeChallengeMethod.IsMissing()) { LogError("Client is missing code challenge or code challenge method", new { clientId = _validatedRequest.ClientId }); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (codeVerifier.IsMissing()) { LogError("Missing code_verifier"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (codeVerifier.Length < _options.InputLengthRestrictions.CodeVerifierMinLength || codeVerifier.Length > _options.InputLengthRestrictions.CodeVerifierMaxLength) { LogError("code_verifier is too short or too long"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (Constants.SupportedCodeChallengeMethods.Contains(idTokenResopnse.Request.CodeChallengeMethod) == false) { LogError("Unsupported code challenge method", new { codeChallengeMethod = idTokenResopnse.Request.CodeChallengeMethod }); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (ValidateCodeVerifierAgainstCodeChallenge(codeVerifier, idTokenResopnse.Request.CodeChallenge, idTokenResopnse.Request.CodeChallengeMethod) == false) { LogError("Transformed code verifier does not match code challenge"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } return(Valid()); }