private async Task <Option <GrantedToken> > HandleApprovedRequest( string issuerName, DeviceAuthorizationData authRequest, CancellationToken cancellationToken) { var scopes = string.Join(" ", authRequest.Scopes); var grantedToken = await _tokenStore.GetValidGrantedToken(_jwksStore, scopes, authRequest.ClientId, cancellationToken) //idTokenJwsPayload: result.AuthCode.IdTokenPayload, //userInfoJwsPayload: result.AuthCode.UserInfoPayLoad) .ConfigureAwait(false); if (grantedToken == null) { var client = await _clientStore.GetById(authRequest.ClientId, cancellationToken).ConfigureAwait(false); grantedToken = await client !.GenerateToken( _jwksStore, authRequest.Scopes, issuerName, //result.AuthCode.UserInfoPayLoad, //result.AuthCode.IdTokenPayload, cancellationToken: cancellationToken //result.AuthCode.IdTokenPayload?.Claims.Where( // c => result.Client.UserClaimsToIncludeInAuthToken.Any(r => r.IsMatch(c.Type))) // .ToArray() //?? Array.Empty<Claim>()) ) .ConfigureAwait(false); await _eventPublisher.Publish( new TokenGranted( Id.Create(), grantedToken.UserInfoPayLoad?.Sub, authRequest.ClientId, string.Join(" ", authRequest.Scopes), GrantTypes.AuthorizationCode, DateTimeOffset.UtcNow)) .ConfigureAwait(false); // Fill-in the id-token if (grantedToken.IdTokenPayLoad != null) { grantedToken = grantedToken with { IdTokenPayLoad = JwtGenerator.UpdatePayloadDate(grantedToken.IdTokenPayLoad, client !.TokenLifetime), IdToken = await client.GenerateIdToken(grantedToken.IdTokenPayLoad, _jwksStore, cancellationToken) .ConfigureAwait(false) }; } await _tokenStore.AddToken(grantedToken !, cancellationToken).ConfigureAwait(false); } return(grantedToken !); } }
public static async Task <string?> GenerateIdToken( this IClientStore clientStore, string clientId, JwtPayload jwsPayload, IJwksStore jwksStore, CancellationToken cancellationToken) { var client = await clientStore.GetById(clientId, cancellationToken).ConfigureAwait(false); return(client == null ? null : await GenerateIdToken(client, jwsPayload, jwksStore, cancellationToken).ConfigureAwait(false)); }
public async Task <IActionResult> RevokeSessionCallback([FromQuery] RevokeSessionRequest?request, CancellationToken cancellationToken) { var authenticatedUser = await _authenticationService.GetAuthenticatedUser(this, CookieNames.CookieName).ConfigureAwait(false); if (authenticatedUser == null || authenticatedUser.Identity?.IsAuthenticated != true) { return(Ok(new { Authenticated = false })); } Response.Cookies.Delete(CoreConstants.SessionId); await _authenticationService.SignOutAsync(HttpContext, CookieNames.CookieName, new AuthenticationProperties()).ConfigureAwait(false); if (request != null && request.post_logout_redirect_uri != null && !string.IsNullOrWhiteSpace(request.id_token_hint)) { var handler = new JwtSecurityTokenHandler(); var jsonWebKeySet = await _jwksStore.GetPublicKeys(cancellationToken).ConfigureAwait(false); var tokenValidationParameters = new TokenValidationParameters { ValidateActor = false, ValidateAudience = false, ValidateIssuer = false, IssuerSigningKeys = jsonWebKeySet.Keys }; handler.ValidateToken(request.id_token_hint, tokenValidationParameters, out var token); var jws = (token as JwtSecurityToken)?.Payload; var claim = jws?.GetClaimValue(StandardClaimNames.Azp); if (claim != null) { var client = await _clientRepository.GetById(claim, cancellationToken).ConfigureAwait(false); if (client?.PostLogoutRedirectUris != null && client.PostLogoutRedirectUris.Any(x => x == request.post_logout_redirect_uri)) { var redirectUrl = request.post_logout_redirect_uri; if (!string.IsNullOrWhiteSpace(request.state)) { redirectUrl = new Uri($"{redirectUrl.AbsoluteUri}?state={request.state}"); } return(Redirect(redirectUrl.AbsoluteUri)); } } } return(Ok(new { Authenticated = true })); }
public async Task <IActionResult> Index(string code, CancellationToken cancellationToken) { var request = _dataProtector.Unprotect <AuthorizationRequest>(code); if (request.client_id == null) { return(BadRequest()); } var authenticatedUser = await SetUser().ConfigureAwait(false); var issuerName = Request.GetAbsoluteUriWithVirtualPath(); var actionResult = await _displayConsent.Execute( request.ToParameter(), authenticatedUser ?? new ClaimsPrincipal(), issuerName, cancellationToken) .ConfigureAwait(false); var result = actionResult.EndpointResult.CreateRedirectionFromActionResult(request, _logger); if (result != null) { return(result); } var client = await _clientStore.GetById(request.client_id, cancellationToken).ConfigureAwait(false); if (client == null) { return(BadRequest()); } var viewModel = new ConsentViewModel { ClientDisplayName = client.ClientName, AllowedScopeDescriptions = actionResult?.Scopes == null ? new List <string>() : actionResult.Scopes.Select(s => s.Description).ToList(), AllowedIndividualClaims = actionResult?.AllowedClaims ?? new List <string>(), LogoUri = client.LogoUri?.AbsoluteUri, PolicyUri = client.PolicyUri?.AbsoluteUri, TosUri = client.TosUri?.AbsoluteUri, Code = code }; return(Ok(viewModel)); }
public static async Task <Option <GrantedToken> > GenerateToken( this IClientStore clientStore, IJwksStore jwksStore, string clientId, string[] scope, string issuerName, CancellationToken cancellationToken, JwtPayload?userInformationPayload = null, JwtPayload?idTokenPayload = null, params Claim[] additionalClaims) { if (string.IsNullOrWhiteSpace(clientId)) { return(new Option <GrantedToken> .Error(new ErrorDetails { Title = ErrorCodes.InvalidClient, Detail = SharedStrings.TheClientDoesntExist, Status = HttpStatusCode.NotFound })); } var client = await clientStore.GetById(clientId, cancellationToken).ConfigureAwait(false); if (client == null) { return(new Option <GrantedToken> .Error(new ErrorDetails { Title = ErrorCodes.InvalidClient, Detail = SharedStrings.TheClientDoesntExist, Status = HttpStatusCode.NotFound })); } var token = await GenerateToken( client, jwksStore, scope, issuerName, userInformationPayload, idTokenPayload, cancellationToken, additionalClaims) .ConfigureAwait(false); return(new Option <GrantedToken> .Result(token)); }
public async Task <Option <Client> > Validate( AuthorizationParameter parameter, CancellationToken cancellationToken) { // Check the required parameters. Read this RFC : http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest if (string.IsNullOrWhiteSpace(parameter.Scope)) { var message = string.Format( Strings.MissingParameter, CoreConstants.StandardAuthorizationRequestParameterNames.ScopeName); _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } if (string.IsNullOrWhiteSpace(parameter.ClientId)) { var message = string.Format( Strings.MissingParameter, CoreConstants.StandardAuthorizationRequestParameterNames.ClientIdName); _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } if (parameter.RedirectUrl == null) { var message = string.Format( Strings.MissingParameter, CoreConstants.StandardAuthorizationRequestParameterNames.RedirectUriName); _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } if (string.IsNullOrWhiteSpace(parameter.ResponseType)) { var message = string.Format( Strings.MissingParameter, CoreConstants.StandardAuthorizationRequestParameterNames.ResponseTypeName); _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } var validationResult = ValidateResponseTypeParameter(parameter.ResponseType, parameter.State); if (validationResult is Option.Error e) { return(new Option <Client> .Error(e.Details, e.State)); } validationResult = ValidatePromptParameter(parameter.Prompt, parameter.State); if (validationResult is Option.Error e2) { return(new Option <Client> .Error(e2.Details, e2.State)); } // With this instruction // The redirect_uri is considered well-formed according to the RFC-3986 var redirectUrlIsCorrect = parameter.RedirectUrl.IsAbsoluteUri; if (!redirectUrlIsCorrect) { var message = Strings.TheRedirectionUriIsNotWellFormed; _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } var client = await _clientRepository.GetById(parameter.ClientId, cancellationToken).ConfigureAwait(false); if (client == null) { var message = string.Format(Strings.ClientIsNotValid, parameter.ClientId); _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } if (!client.GetRedirectionUrls(parameter.RedirectUrl).Any()) { var message = string.Format(Strings.RedirectUrlIsNotValid, parameter.RedirectUrl); _logger.LogError(message); return(new Option <Client> .Error( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = message, Status = HttpStatusCode.BadRequest }, parameter.State)); } return(new Option <Client> .Result(client)); }
/// <summary> /// Fetch the scopes and client name from the ClientRepository and the parameter /// Those information are used to create the consent screen. /// </summary> /// <param name="authorizationParameter">Authorization code grant type parameter.</param> /// <param name="claimsPrincipal"></param> /// <param name="issuerName"></param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the async operation.</param> /// <returns>Action resultKind.</returns> public async Task <DisplayContentResult> Execute( AuthorizationParameter authorizationParameter, ClaimsPrincipal claimsPrincipal, string issuerName, CancellationToken cancellationToken) { var client = authorizationParameter.ClientId == null ? null : await _clientRepository.GetById(authorizationParameter.ClientId, cancellationToken) .ConfigureAwait(false); if (client == null) { return(new DisplayContentResult( EndpointResult.CreateBadRequestResult( new ErrorDetails { Title = ErrorCodes.InvalidRequest, Detail = string.Format(Strings.ClientIsNotValid, authorizationParameter.ClientId), Status = HttpStatusCode.BadRequest }))); } EndpointResult endpointResult; var subject = claimsPrincipal.GetSubject() !; var assignedConsent = await _consentRepository .GetConfirmedConsents(subject, authorizationParameter, cancellationToken) .ConfigureAwait(false); // If there's already a consent then redirect to the callback if (assignedConsent != null) { endpointResult = await _generateAuthorizationResponse.Generate( EndpointResult.CreateAnEmptyActionResultWithRedirectionToCallBackUrl(), authorizationParameter, claimsPrincipal, client, issuerName, cancellationToken) .ConfigureAwait(false); var responseMode = authorizationParameter.ResponseMode; if (responseMode == ResponseModes.None) { var responseTypes = authorizationParameter.ResponseType.ParseResponseTypes(); var authorizationFlow = GetAuthorizationFlow(responseTypes, authorizationParameter.State); switch (authorizationFlow) { case Option <AuthorizationFlow> .Error e: return(new DisplayContentResult(EndpointResult.CreateBadRequestResult(e.Details))); case Option <AuthorizationFlow> .Result r: responseMode = GetResponseMode(r.Item); break; } } endpointResult = endpointResult with { RedirectInstruction = endpointResult.RedirectInstruction !with { ResponseMode = responseMode } }; return(new DisplayContentResult(endpointResult)); } ICollection <string> allowedClaims = Array.Empty <string>(); ICollection <Scope> allowedScopes = Array.Empty <Scope>(); var claimsParameter = authorizationParameter.Claims; if (claimsParameter.IsAnyIdentityTokenClaimParameter() || claimsParameter.IsAnyUserInfoClaimParameter()) { allowedClaims = claimsParameter.GetClaimNames(); } else { allowedScopes = (await GetScopes(authorizationParameter.Scope !, cancellationToken).ConfigureAwait(false)) .Where(s => s.IsDisplayedInConsent) .ToList(); } endpointResult = EndpointResult.CreateAnEmptyActionResultWithOutput(); return(new DisplayContentResult(client, allowedScopes, allowedClaims, endpointResult)); }
/// <summary> /// Authenticates the specified instruction. /// </summary> /// <param name="instruction">The instruction.</param> /// <param name="issuerName">Name of the issuer.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">instruction</exception> public async Task <AuthenticationResult> Authenticate( AuthenticateInstruction instruction, string issuerName, CancellationToken cancellationToken) { Client?client = null; // First we try to fetch the client_id // The different client authentication mechanisms are described here : http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication var clientId = TryGettingClientId(instruction); if (!string.IsNullOrWhiteSpace(clientId)) { client = await _clientRepository.GetById(clientId, cancellationToken).ConfigureAwait(false); } if (client == null) { return(new AuthenticationResult(null, SharedStrings.TheClientDoesntExist)); } var errorMessage = string.Empty; switch (client.TokenEndPointAuthMethod) { case TokenEndPointAuthenticationMethods.ClientSecretBasic: client = instruction.AuthenticateClient(client); if (client == null) { errorMessage = Strings.TheClientCannotBeAuthenticatedWithSecretBasic; } break; case TokenEndPointAuthenticationMethods.ClientSecretPost: client = ClientSecretPostAuthentication.AuthenticateClient(instruction, client); if (client == null) { errorMessage = Strings.TheClientCannotBeAuthenticatedWithSecretPost; } break; case TokenEndPointAuthenticationMethods.ClientSecretJwt: if (client.Secrets.All(s => s.Type != ClientSecretTypes.SharedSecret)) { errorMessage = string.Format( Strings.TheClientDoesntContainASharedSecret, client.ClientId); break; } return(await _clientAssertionAuthentication.AuthenticateClientWithClientSecretJwt(instruction, cancellationToken) .ConfigureAwait(false)); case TokenEndPointAuthenticationMethods.PrivateKeyJwt: return(await _clientAssertionAuthentication .AuthenticateClientWithPrivateKeyJwt(instruction, issuerName, cancellationToken) .ConfigureAwait(false)); case TokenEndPointAuthenticationMethods.TlsClientAuth: client = AuthenticateTlsClient(instruction, client); if (client == null) { errorMessage = Strings.TheClientCannotBeAuthenticatedWithTls; } break; } return(new AuthenticationResult(client, errorMessage)); }
public async Task <EndpointResult> ProcessRedirection( AuthorizationParameter authorizationParameter, string?code, string subject, Claim[] claims, string?issuerName, CancellationToken cancellationToken) { var client = authorizationParameter.ClientId == null ? null : await _clientRepository.GetById(authorizationParameter.ClientId, cancellationToken) .ConfigureAwait(false); if (client == null) { throw new InvalidOperationException(SharedStrings.TheClientDoesntExist); } // Redirect to the consent page if the prompt parameter contains "consent" EndpointResult result; var prompts = authorizationParameter.Prompt.ParsePrompts(); if (prompts.Contains(PromptParameters.Consent) && code != null) { return(EndpointResult.CreateAnEmptyActionResultWithRedirection( SimpleAuthEndPoints.ConsentIndex, new Parameter("code", code))); } var assignedConsent = await _consentRepository .GetConfirmedConsents(subject, authorizationParameter, cancellationToken) .ConfigureAwait(false); // If there's already one consent then redirect to the callback if (assignedConsent != null) { var claimsIdentity = new ClaimsIdentity(claims, "SimpleAuth"); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); result = await _generateAuthorizationResponse.Generate( EndpointResult.CreateAnEmptyActionResultWithRedirectionToCallBackUrl(), authorizationParameter, claimsPrincipal, client, issuerName, cancellationToken) .ConfigureAwait(false); var responseMode = authorizationParameter.ResponseMode; if (responseMode != ResponseModes.None) { return(result with { RedirectInstruction = result.RedirectInstruction !with { ResponseMode = responseMode } }); } var responseTypes = authorizationParameter.ResponseType.ParseResponseTypes(); var authorizationFlow = GetAuthorizationFlow(authorizationParameter.State, responseTypes); switch (authorizationFlow) { case Option <AuthorizationFlow> .Error e: return(EndpointResult.CreateBadRequestResult(e.Details)); case Option <AuthorizationFlow> .Result r: responseMode = GetResponseMode(r.Item); break; } return(result with { RedirectInstruction = result.RedirectInstruction !with { ResponseMode = responseMode } }); } // If there's no consent & there's no consent prompt then redirect to the consent screen. return(EndpointResult.CreateAnEmptyActionResultWithRedirection( SimpleAuthEndPoints.ConsentIndex, new Parameter("code", code ?? ""))); }
/// <summary> /// This method is executed when the user confirm the consent /// 1). If there's already consent confirmed in the past by the resource owner /// 1).* then we generate an authorization code and redirects to the callback. /// 2). If there's no consent then we insert it and the authorization code is returned /// 2°.* to the callback url. /// </summary> /// <param name="authorizationParameter">Authorization code grant-type</param> /// <param name="claimsPrincipal">Resource owner's claims</param> /// <param name="issuerName"></param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the async operation.</param> /// <returns>Redirects the authorization code to the callback.</returns> public async Task <EndpointResult> Execute( AuthorizationParameter authorizationParameter, ClaimsPrincipal claimsPrincipal, string issuerName, CancellationToken cancellationToken) { var client = authorizationParameter.ClientId == null ? null : await _clientRepository.GetById(authorizationParameter.ClientId, cancellationToken) .ConfigureAwait(false); if (client == null) { throw new InvalidOperationException( string.Format( Strings.TheClientIdDoesntExist, authorizationParameter.ClientId)); } var subject = claimsPrincipal.GetSubject() !; var assignedConsent = await _consentRepository .GetConfirmedConsents(subject, authorizationParameter, cancellationToken) .ConfigureAwait(false); // Insert a new consent. if (assignedConsent == null) { var claimsParameter = authorizationParameter.Claims; if (claimsParameter.IsAnyIdentityTokenClaimParameter() || claimsParameter.IsAnyUserInfoClaimParameter()) { // A consent can be given to a set of claims assignedConsent = new Consent { Id = Id.Create(), ClientId = client.ClientId, ClientName = client.ClientName, Subject = subject, Claims = claimsParameter.GetClaimNames() }; } else { // A consent can be given to a set of scopes assignedConsent = new Consent { Id = Id.Create(), ClientId = client.ClientId, ClientName = client.ClientName, GrantedScopes = authorizationParameter.Scope == null ? Array.Empty <string>() : (await GetScopes(authorizationParameter.Scope, cancellationToken) .ConfigureAwait(false)).ToArray(), Subject = subject, }; } // A consent can be given to a set of claims await _consentRepository.Insert(assignedConsent, cancellationToken).ConfigureAwait(false); await _eventPublisher.Publish( new ConsentAccepted( Id.Create(), subject, client.ClientId, assignedConsent.GrantedScopes, DateTimeOffset.UtcNow)) .ConfigureAwait(false); } var result = await _generateAuthorizationResponse.Generate( EndpointResult.CreateAnEmptyActionResultWithRedirectionToCallBackUrl(), authorizationParameter, claimsPrincipal, client, issuerName, cancellationToken) .ConfigureAwait(false); // If redirect to the callback and the response mode has not been set. if (result.Type != ActionResultType.RedirectToCallBackUrl) { return(result); } var responseMode = authorizationParameter.ResponseMode; if (responseMode == ResponseModes.None) { var responseTypes = authorizationParameter.ResponseType.ParseResponseTypes(); var authorizationFlow = GetAuthorizationFlow(responseTypes, authorizationParameter.State); switch (authorizationFlow) { case Option <AuthorizationFlow> .Error e: return(EndpointResult.CreateBadRequestResult(e.Details)); case Option <AuthorizationFlow> .Result r: responseMode = GetResponseMode(r.Item); break; } } result = result with { RedirectInstruction = result.RedirectInstruction !with { ResponseMode = responseMode } }; return(result); }