private async Task <AuthenticationTicket> InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync( OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc) { TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest; await Options.Provider.ValidateTokenRequest(validatingContext); var grantContext = new OAuthGrantResourceOwnerCredentialsContext( Context, Options, validatingContext.ClientContext.ClientId, tokenEndpointRequest.ResourceOwnerPasswordCredentialsGrant.UserName, tokenEndpointRequest.ResourceOwnerPasswordCredentialsGrant.Password, tokenEndpointRequest.ResourceOwnerPasswordCredentialsGrant.Scope); if (validatingContext.IsValidated) { await Options.Provider.GrantResourceOwnerCredentialsAsync(grantContext); } return(ReturnOutcome( validatingContext, grantContext, grantContext.Ticket, Constants.Errors.InvalidGrant)); }
private async Task <AuthenticationTicket> InvokeTokenEndpointCustomGrantAsync( OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc) { TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest; await Options.Provider.ValidateTokenRequest(validatingContext); var grantContext = new OAuthGrantCustomExtensionContext( Context, Options, validatingContext.ClientContext.ClientId, tokenEndpointRequest.GrantType, tokenEndpointRequest.CustomExtensionGrant.Parameters); if (validatingContext.IsValidated) { await Options.Provider.GrantCustomExtension(grantContext); } return(ReturnOutcome( validatingContext, grantContext, grantContext.Ticket, Constants.Errors.UnsupportedGrantType)); }
private async Task <AuthenticationTicket> InvokeTokenEndpointClientCredentialsGrantAsync( OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc) { TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest; await Options.Provider.ValidateTokenRequest(validatingContext); if (!validatingContext.IsValidated) { return(null); } var grantContext = new OAuthGrantClientCredentialsContext( Context, Options, validatingContext.ClientContext.ClientId, tokenEndpointRequest.ClientCredentialsGrant.Scope); await Options.Provider.GrantClientCredentials(grantContext); return(ReturnOutcome( validatingContext, grantContext, grantContext.Ticket, Constants.Errors.UnauthorizedClient)); }
/// <summary> /// Initializes a new instance of the <see cref="OAuthValidateTokenRequestContext"/> class /// </summary> /// <param name="context"></param> /// <param name="options"></param> /// <param name="tokenRequest"></param> /// <param name="clientContext"></param> public OAuthValidateTokenRequestContext( IOwinContext context, OAuthAuthorizationServerOptions options, TokenEndpointRequest tokenRequest, BaseValidatingClientContext clientContext) : base(context, options) { TokenRequest = tokenRequest; ClientContext = clientContext; }
private async Task <AuthenticationTicket> InvokeTokenEndpointRefreshTokenGrantAsync( OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc) { TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest; var refreshTokenContext = new AuthenticationTokenReceiveContext( Context, Options.RefreshTokenFormat, tokenEndpointRequest.RefreshTokenGrant.RefreshToken); await Options.RefreshTokenProvider.ReceiveAsync(refreshTokenContext); AuthenticationTicket ticket = refreshTokenContext.Ticket; if (ticket == null) { _logger.WriteError("invalid refresh token"); validatingContext.SetError(Constants.Errors.InvalidGrant); return(null); } if (!ticket.Properties.ExpiresUtc.HasValue || ticket.Properties.ExpiresUtc < currentUtc) { _logger.WriteError("expired refresh token"); validatingContext.SetError(Constants.Errors.InvalidGrant); return(null); } await Options.Provider.ValidateTokenRequest(validatingContext); var grantContext = new OAuthGrantRefreshTokenContext(Context, Options, ticket, validatingContext.ClientContext.ClientId); if (validatingContext.IsValidated) { await Options.Provider.GrantRefreshToken(grantContext); } return(ReturnOutcome( validatingContext, grantContext, grantContext.Ticket, Constants.Errors.InvalidGrant)); }
/// <summary> /// Initializes a new instance of the <see cref="OAuthTokenEndpointContext"/> class /// </summary> /// <param name="context"></param> /// <param name="options"></param> /// <param name="ticket"></param> /// <param name="tokenEndpointRequest"></param> public OAuthTokenEndpointContext( IOwinContext context, OAuthAuthorizationServerOptions options, AuthenticationTicket ticket, TokenEndpointRequest tokenEndpointRequest) : base(context, options) { if (ticket == null) { throw new ArgumentNullException("ticket"); } Identity = ticket.Identity; Properties = ticket.Properties; TokenEndpointRequest = tokenEndpointRequest; AdditionalResponseParameters = new Dictionary <string, object>(StringComparer.Ordinal); TokenIssued = Identity != null; }
private async Task <AuthenticationTicket> InvokeTokenEndpointAuthorizationCodeGrantAsync( OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc) { TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest; var authorizationCodeContext = new AuthenticationTokenReceiveContext( Context, Options.AuthorizationCodeFormat, tokenEndpointRequest.AuthorizationCodeGrant.Code); await Options.AuthorizationCodeProvider.ReceiveAsync(authorizationCodeContext); AuthenticationTicket ticket = authorizationCodeContext.Ticket; if (ticket == null) { _logger.WriteError("invalid authorization code"); validatingContext.SetError(Constants.Errors.InvalidGrant); return(null); } if (!ticket.Properties.ExpiresUtc.HasValue || ticket.Properties.ExpiresUtc < currentUtc) { _logger.WriteError("expired authorization code"); validatingContext.SetError(Constants.Errors.InvalidGrant); return(null); } string clientId; if (!ticket.Properties.Dictionary.TryGetValue(Constants.Extra.ClientId, out clientId) || !String.Equals(clientId, validatingContext.ClientContext.ClientId, StringComparison.Ordinal)) { _logger.WriteError("authorization code does not contain matching client_id"); validatingContext.SetError(Constants.Errors.InvalidGrant); return(null); } string redirectUri; if (ticket.Properties.Dictionary.TryGetValue(Constants.Extra.RedirectUri, out redirectUri)) { ticket.Properties.Dictionary.Remove(Constants.Extra.RedirectUri); if (!String.Equals(redirectUri, tokenEndpointRequest.AuthorizationCodeGrant.RedirectUri, StringComparison.Ordinal)) { _logger.WriteError("authorization code does not contain matching redirect_uri"); validatingContext.SetError(Constants.Errors.InvalidGrant); return(null); } } await Options.Provider.ValidateTokenRequest(validatingContext); var grantContext = new OAuthGrantAuthorizationCodeContext( Context, Options, ticket); if (validatingContext.IsValidated) { await Options.Provider.GrantAuthorizationCode(grantContext); } return(ReturnOutcome( validatingContext, grantContext, grantContext.Ticket, Constants.Errors.InvalidGrant)); }
private async Task InvokeTokenEndpointAsync() { DateTimeOffset currentUtc = Options.SystemClock.UtcNow; // remove milliseconds in case they don't round-trip currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond)); IFormCollection form = await Request.ReadFormAsync(); var clientContext = new OAuthValidateClientAuthenticationContext( Context, Options, form); await Options.Provider.ValidateClientAuthenticationAsync(clientContext); if (!clientContext.IsValidated) { _logger.WriteError("clientID is not valid."); if (!clientContext.HasError) { clientContext.SetError(Constants.Errors.InvalidClient); } await SendErrorAsJsonAsync(clientContext); return; } var tokenEndpointRequest = new TokenEndpointRequest(form); var validatingContext = new OAuthValidateTokenRequestContext(Context, Options, tokenEndpointRequest, clientContext); AuthenticationTicket ticket = null; if (tokenEndpointRequest.IsAuthorizationCodeGrantType) { // Authorization Code Grant http://tools.ietf.org/html/rfc6749#section-4.1 // Access Token Request http://tools.ietf.org/html/rfc6749#section-4.1.3 ticket = await InvokeTokenEndpointAuthorizationCodeGrantAsync(validatingContext, currentUtc); } else if (tokenEndpointRequest.IsResourceOwnerPasswordCredentialsGrantType) { // Resource Owner Password Credentials Grant http://tools.ietf.org/html/rfc6749#section-4.3 // Access Token Request http://tools.ietf.org/html/rfc6749#section-4.3.2 ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(validatingContext, currentUtc); } else if (tokenEndpointRequest.IsClientCredentialsGrantType) { // Client Credentials Grant http://tools.ietf.org/html/rfc6749#section-4.4 // Access Token Request http://tools.ietf.org/html/rfc6749#section-4.4.2 ticket = await InvokeTokenEndpointClientCredentialsGrantAsync(validatingContext, currentUtc); } else if (tokenEndpointRequest.IsRefreshTokenGrantType) { // Refreshing an Access Token // http://tools.ietf.org/html/rfc6749#section-6 ticket = await InvokeTokenEndpointRefreshTokenGrantAsync(validatingContext, currentUtc); } else if (tokenEndpointRequest.IsCustomExtensionGrantType) { // Defining New Authorization Grant Types // http://tools.ietf.org/html/rfc6749#section-8.3 ticket = await InvokeTokenEndpointCustomGrantAsync(validatingContext, currentUtc); } else { // Error Response http://tools.ietf.org/html/rfc6749#section-5.2 // The authorization grant type is not supported by the // authorization server. _logger.WriteError("grant type is not recognized"); validatingContext.SetError(Constants.Errors.UnsupportedGrantType); } if (ticket == null) { await SendErrorAsJsonAsync(validatingContext); return; } ticket.Properties.IssuedUtc = currentUtc; ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan); var tokenEndpointContext = new OAuthTokenEndpointContext( Context, Options, ticket, tokenEndpointRequest); await Options.Provider.TokenEndpoint(tokenEndpointContext); if (tokenEndpointContext.TokenIssued) { ticket = new AuthenticationTicket( tokenEndpointContext.Identity, tokenEndpointContext.Properties); } else { _logger.WriteError("Token was not issued to tokenEndpointContext"); validatingContext.SetError(Constants.Errors.InvalidGrant); await SendErrorAsJsonAsync(validatingContext); return; } var accessTokenContext = new AuthenticationTokenCreateContext( Context, Options.AccessTokenFormat, ticket); await Options.AccessTokenProvider.CreateAsync(accessTokenContext); string accessToken = accessTokenContext.Token; if (string.IsNullOrEmpty(accessToken)) { accessToken = accessTokenContext.SerializeTicket(); } DateTimeOffset?accessTokenExpiresUtc = ticket.Properties.ExpiresUtc; var refreshTokenCreateContext = new AuthenticationTokenCreateContext( Context, Options.RefreshTokenFormat, accessTokenContext.Ticket); await Options.RefreshTokenProvider.CreateAsync(refreshTokenCreateContext); string refreshToken = refreshTokenCreateContext.Token; var tokenEndpointResponseContext = new OAuthTokenEndpointResponseContext( Context, Options, ticket, tokenEndpointRequest, accessToken, tokenEndpointContext.AdditionalResponseParameters); await Options.Provider.TokenEndpointResponse(tokenEndpointResponseContext); var memory = new MemoryStream(); byte[] body; using (var writer = new JsonTextWriter(new StreamWriter(memory))) { writer.WriteStartObject(); writer.WritePropertyName(Constants.Parameters.AccessToken); writer.WriteValue(accessToken); writer.WritePropertyName(Constants.Parameters.TokenType); writer.WriteValue(Constants.TokenTypes.Bearer); if (accessTokenExpiresUtc.HasValue) { TimeSpan?expiresTimeSpan = accessTokenExpiresUtc - currentUtc; var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds; if (expiresIn > 0) { writer.WritePropertyName(Constants.Parameters.ExpiresIn); writer.WriteValue(expiresIn); } } if (!String.IsNullOrEmpty(refreshToken)) { writer.WritePropertyName(Constants.Parameters.RefreshToken); writer.WriteValue(refreshToken); } foreach (var additionalResponseParameter in tokenEndpointResponseContext.AdditionalResponseParameters) { writer.WritePropertyName(additionalResponseParameter.Key); writer.WriteValue(additionalResponseParameter.Value); } writer.WriteEndObject(); writer.Flush(); body = memory.ToArray(); } Response.ContentType = "application/json;charset=UTF-8"; Response.Headers.Set("Cache-Control", "no-cache"); Response.Headers.Set("Pragma", "no-cache"); Response.Headers.Set("Expires", "-1"); Response.ContentLength = body.Length; await Response.WriteAsync(body, Request.CallCancelled); }
/// <summary> /// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are /// present on the request. If the web application accepts Basic authentication credentials, /// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web /// application accepts "client_id" and "client_secret" as form encoded POST parameters, /// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body. /// If context.Validated is not called the request will not proceed further. /// </summary> /// <param name="context">The context of the event carries information in and results out.</param> /// <returns>Task to enable asynchronous execution</returns> public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { //TODO: This would work but would it be better to just have two different endpoints setup - one for members, one for users. // Each of those would then support the "password", "authorization_code" grant_type //check for a custom parameter in the request which will dictate how with authenticate var uid = context.Parameters.Where(f => f.Key == "umb_auth").Select(f => f.Value).SingleOrDefault(); if (uid == null || uid.Length == 0) { uid = new[] { UmbracoAuthType.UserPassword.ToString() }; } context.OwinContext.Set("umb:authtype", uid[0]); var tokenEndpointRequest = new TokenEndpointRequest(context.Parameters); if (tokenEndpointRequest.IsAuthorizationCodeGrantType) { if (!context.TryGetBasicCredentials(out var clientId, out _)) { context.TryGetFormCredentials(out clientId, out _); } if (context.ClientId == null) { context.Rejected(); context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header."); return(Task.FromResult(0)); } context.Validated(clientId); } else { context.Validated(); } return(Task.FromResult(0)); // Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are // present on the request. If the web application accepts Basic authentication credentials, // context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web // application accepts "client_id" and "client_secret" as form encoded POST parameters, // context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body. // If context.Validated is not called the request will not proceed further. //** Currently we just accept everything globally //context.Validated(); //return Task.FromResult(0); // Example for checking registered clients: //** Validate that the data is in the request //string clientId; //string clientSecret; //if (context.TryGetFormCredentials(out clientId, out clientSecret) == false) //{ // context.SetError("invalid_client", "Form credentials could not be retrieved."); // context.Rejected(); // return Task.FromResult(0); //} //var userManager = context.OwinContext.GetUserManager<BackOfficeUserManager>(); //** Check if this client id is allowed/registered // - lookup in custom table //** Verify that the client id and client secret match //if (client != null && userManager.PasswordHasher.VerifyHashedPassword(client.ClientSecretHash, clientSecret) == PasswordVerificationResult.Success) //{ // // Client has been verified. // context.Validated(clientId); //} //else //{ // // Client could not be validated. // context.SetError("invalid_client", "Client credentials are invalid."); // context.Rejected(); //} }