protected override Task <AuthenticateResult> HandleAuthenticateAsync() { var header = Request.Headers["Authorization"].ToString(); if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ")) { return(Task.FromResult(AuthenticateResult.Skip())); } var userName = header.Substring(7); var claims = new List <Claim>(); claims.Add(new Claim(ClaimTypes.Name, userName, ClaimValueTypes.String, "https://contoso.com/")); var identity = new ClaimsIdentity( claims, Options.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket( principal, new AuthenticationProperties(), Options.AuthenticationScheme); return(Task.FromResult(AuthenticateResult.Success(ticket))); }
/// <summary> /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options. /// </summary> /// <returns></returns> protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var token = ExtractToken(Request); // If no token found, no further work possible if (string.IsNullOrEmpty(token)) { return(AuthenticateResult.Skip()); } var validationParameters = Options.TokenValidationParameters.Clone(); SecurityToken validatedToken; var validators = Options.SecurityTokenValidatorsFactory(); foreach (var validator in validators) { if (validator.CanReadToken(token)) { var principal = validator.ValidateToken(token, validationParameters, out validatedToken); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); return(AuthenticateResult.Success(ticket)); } } // Ugly patch to make this method should to be async in order to allow result caching by caller await DoneTask; // Not so nice, but AuthenticateResult.Fail does not allow us to show the error throw new AuthenticationException("Authorization token has been detected but it cannot be read."); }
private async Task <AuthenticateResult> ReadHeaderTicket() { if (!Context.Request.Headers.ContainsKey("Authorization")) { return(AuthenticateResult.Skip()); } var headerValue = Context.Request.Headers["Authorization"].First(); var ticket = Options.TicketDataFormat.Unprotect(headerValue, GetTlsTokenBinding()); if (ticket == null) { return(AuthenticateResult.Fail("Unprotect ticket failed")); } var currentUtc = DateTimeOffset.UtcNow; var issuedUtc = ticket.Properties.IssuedUtc; var expiresUtc = ticket.Properties.ExpiresUtc; if (expiresUtc != null && expiresUtc.Value < currentUtc) { return(AuthenticateResult.Fail("Ticket expired")); } return(AuthenticateResult.Success(ticket)); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { AuthenticateResult result = null; bool isValid = true; if (isValid) { //assigning fake identity, just for illustration ClaimsIdentity claimsIdentity = new ClaimsIdentity("Custom"); var claims = new List <Claim>(); claims.Add(new Claim(ClaimTypes.Name, "admin")); claims.Add(new Claim(ClaimTypes.GivenName, "God")); claims.Add(new Claim(ClaimTypes.Email, "*****@*****.**")); claims.Add(new Claim(ClaimTypes.NameIdentifier, "admin")); claims.Add(new Claim(ClaimTypes.Role, "admin")); claims.Add(new Claim("oko:zone", "*")); claimsIdentity.AddClaims(claims); ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity); result = AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, new AuthenticationProperties(), Options.AuthenticationScheme)); } else { result = AuthenticateResult.Skip(); } return(result); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var request = Context.GetOpenIdConnectRequest(); if (request == null) { throw new InvalidOperationException("An identity cannot be extracted from this request."); } if (request.IsAuthorizationRequest() || request.IsLogoutRequest()) { if (string.IsNullOrEmpty(request.IdTokenHint)) { return(AuthenticateResult.Skip()); } var ticket = await DeserializeIdentityTokenAsync(request.IdTokenHint, request); if (ticket == null) { Logger.LogWarning("The identity token extracted from the id_token_hint " + "parameter was invalid and has been ignored."); return(AuthenticateResult.Skip()); } // Tickets are returned even if they // are considered invalid (e.g expired). return(AuthenticateResult.Success(ticket)); } throw new InvalidOperationException("An identity cannot be extracted from this request."); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { string token = null; AuthenticateResult result = null; string token = Helper.GetTokenFromHEader(Request.Headers["Authorization"]); // If no token found, no further work possible if (string.IsNullOrEmpty(token)) { result = AuthenticateResult.Skip(); } else { bool isValid = await _tokenService.IsValidAsync(token); if (isValid) { ClaimsIdentity claimsIdentity = new ClaimsIdentity("Custom"); var claims = new List <Claim>(); claims.Add(new Claim(ClaimTypes.Name, "admin")); claims.Add(new Claim(ClaimTypes.NameIdentifier, "admin")); claims.Add(new Claim(ClaimTypes.Role, "admin")); ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity); result = AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, new AuthenticationProperties(), Options.AuthenticationScheme)); } else { result = AuthenticateResult.Skip(); } } return(result); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var keyword = Options.AuthenticationScheme + " "; string authorization = Request.Headers["Authorization"]; if (string.IsNullOrEmpty(authorization)) { return(await Task.FromResult(AuthenticateResult.Skip())); } if (!authorization.StartsWith(keyword)) { return(await Task.FromResult(AuthenticateResult.Skip())); } var authValue = authorization.Substring(keyword.Length).Trim(); if (string.IsNullOrEmpty(authValue)) { return(await Task.FromResult(AuthenticateResult.Skip())); } if (authValue.Equals(Options.SecurityKey)) { return(await Task.FromResult(AuthenticateResult.Success( new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultRoleClaimType, Roles.MatRegisteredUser, ClaimValueTypes.String) }, Options.AuthenticationScheme)), new AuthenticationProperties(), Options.AuthenticationScheme)))); } else { return(await Task.FromResult(AuthenticateResult.Fail("Incorrenct key"))); } }
/// <inheritdoc /> protected override sealed async Task <AuthenticateResult> HandleAuthenticateAsync() { // get authorization header string auth = Request.Headers[AuthorizationHeader]; if (auth == null) { return(AuthenticateResult.Skip()); } // confirm authorization scheme string[] authParts = auth.Split(' '); if (authParts.Length != 2) { return(AuthenticateResult.Fail("Invalid authorization header")); } else if (!ShouldHandleScheme(authParts[0], Options.AutomaticAuthenticate)) { return(AuthenticateResult.Fail($"Authorization scheme \"{authParts[0]}\" is not supported")); } // extract user and password string base64 = authParts[1]; string authValue; try { byte[] bytes = Convert.FromBase64String(base64); authValue = Encoding.ASCII.GetString(bytes); } catch { authValue = null; } if (string.IsNullOrEmpty(authValue)) { return(AuthenticateResult.Fail("Invalid authorization header base64 value")); } // find and validate user var user = await HandleAuthenticateHeaderAsync(authValue); if (user == null) { return(AuthenticateResult.Fail("User was not found")); } string validationResult = await ValidateUserAsync(user); if (validationResult != null) { return(AuthenticateResult.Fail(validationResult)); } // create authentication ticket var ticket = await CreateAuthenticationTicketAsync(user); return(AuthenticateResult.Success(ticket)); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { if (Context.User.Identity.IsAuthenticated) { return(await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Options.AuthenticationScheme)))); } return(await Task.FromResult(AuthenticateResult.Skip())); }
protected override Task <AuthenticateResult> HandleAuthenticateAsync() { var authorizationHeader = Context.Request.Headers["Authorization"]; if (!authorizationHeader.Any()) { return(Task.FromResult(AuthenticateResult.Skip())); } var value = authorizationHeader.ToString(); if (string.IsNullOrWhiteSpace(value)) { return(Task.FromResult(AuthenticateResult.Skip())); } // place logic here to validate the header value (decrypt, call db etc) var token = value.Replace(Scheme.Custom, string.Empty).Trim(); var authorization = Encoding.UTF8.GetString(Convert.FromBase64String(token)).Split(':'); if (!authorization.Any()) { return(Task.FromResult(AuthenticateResult.Skip())); } if (authorization.Length != 2) { return(Task.FromResult(AuthenticateResult.Skip())); } var user = authorization[0]; var pass = authorization[1]; var valid = user.Equals("Aladdin") && pass.Equals("open sesame"); if (!valid) { return(Task.FromResult(AuthenticateResult.Skip())); } // create a new claims identity and return an AuthenticationTicket // with the correct scheme var claims = new[] { new Claim(ClaimTypes.Name, user) }; var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Custom)); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Custom); return(Task.FromResult(AuthenticateResult.Success(ticket))); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { if (Request.Query.ContainsKey("$tweeklegacy")) { return(AuthenticateResult.Success( new AuthenticationTicket( new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("iss", "TweekLegacy") })), null, "TweekLegacy"))); } return(AuthenticateResult.Skip()); }
private async Task <AuthenticateResult> ReadCookieTicket() { var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName); if (string.IsNullOrEmpty(cookie)) { return(AuthenticateResult.Skip()); } var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding()); if (ticket == null) { return(AuthenticateResult.Fail("Unprotect ticket failed")); } if (Options.SessionStore != null) { var claim = ticket.Principal.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim)); if (claim == null) { return(AuthenticateResult.Fail("SessionId missing")); } _sessionKey = claim.Value; ticket = await Options.SessionStore.RetrieveAsync(_sessionKey); if (ticket == null) { return(AuthenticateResult.Fail("Identity missing in session store")); } } var currentUtc = DateTimeOffset.UtcNow; var issuedUtc = ticket.Properties.IssuedUtc; var expiresUtc = ticket.Properties.ExpiresUtc; if (expiresUtc != null && expiresUtc.Value < currentUtc) { if (Options.SessionStore != null) { await Options.SessionStore.RemoveAsync(_sessionKey); } return(AuthenticateResult.Fail("Ticket expired")); } CheckForRefresh(ticket); // Finally we have a valid ticket return(AuthenticateResult.Success(ticket)); }
private Task <AuthenticateResult> ReadPlatformTicket() { //读取平台Cookie var platformCookie = Options.CookieManager.GetRequestCookie(Context, Options.PlatformName); if (string.IsNullOrEmpty(platformCookie)) { return(Task.FromResult(AuthenticateResult.Skip())); } //==============================模拟操作,================================= //此处应该和平台约定一套加密解密的算法,然后实现掉IDataProtectionProvider接口 var principal = new ClaimsPrincipal(); var authProp = new AuthenticationProperties(); var claims = new List <Claim>(); claims.Add(new Claim(ClaimTypes.Name, "zhangsan")); claims.Add(new Claim(ClaimTypes.Email, "*****@*****.**")); var identity = new ClaimsIdentity(claims, Options.PlatformName); principal.AddIdentity(identity); principal.Claims.Append(new Claim(SessionIdClaim, "SessionId")); var myTicket = new AuthenticationTicket(principal, authProp, Options.AuthenticationScheme); var myPlatformCookie = Options.TicketDataFormat.Protect(myTicket); //==============================END 模拟操作================================= //解析Cookie中加密的票据 var ticket = Options.TicketDataFormat.Unprotect(myPlatformCookie); if (ticket == null) { return(Task.FromResult(AuthenticateResult.Fail("解密平台票据失败"))); } //验证票据是否过期或有效 var issuedUtc = ticket.Properties.IssuedUtc; var expiresUtc = ticket.Properties.ExpiresUtc; if (issuedUtc != null && expiresUtc != null && expiresUtc.Value < DateTime.UtcNow) { return(Task.FromResult((AuthenticateResult.Fail("票据过期")))); } return(Task.FromResult(AuthenticateResult.Success(ticket))); }
protected override Task <AuthenticateResult> HandleAuthenticateAsync() { var ticketData = Context.Session.GetString(Options.CookieName(Context)); if (ticketData == null) { return(Task.FromResult(AuthenticateResult.Skip())); } try { var ticket = Options.TicketDataFormat.Unprotect(ticketData, GetTlsTokenBinding()); return(Task.FromResult(AuthenticateResult.Success(ticket))); } catch (Exception ex) { return(Task.FromResult(AuthenticateResult.Fail(ex))); } }
protected override Task <AuthenticateResult> HandleAuthenticateAsync() { string cookieValue = Request.Cookies[Options.CookieName(Context)]; if (cookieValue == null) { return(Task.FromResult(AuthenticateResult.Skip())); } try { var ticket = Options.TicketDataFormat.Unprotect(cookieValue, GetTlsTokenBinding()); return(Task.FromResult(AuthenticateResult.Success(ticket))); } catch (Exception ex) { return(Task.FromResult(AuthenticateResult.Fail(ex))); } }
private async Task <AuthenticateResult> ReadAuthorizationHeader() { string token = null; AuthenticateResult result = null; // Give application opportunity to find from a different location, adjust, or reject token var messageReceivedContext = new MessageReceivedContext(Context, Options); // event can set the token await Options.Events.MessageReceived(messageReceivedContext); if (messageReceivedContext.CheckEventResult(out result)) { return(result); } // If application retrieved token from somewhere else, use that. token = messageReceivedContext.Token; if (string.IsNullOrEmpty(token)) { string authorization = Request.Headers["Authorization"]; // If no authorization header found, nothing to process further if (string.IsNullOrEmpty(authorization)) { return(AuthenticateResult.Skip()); } if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = authorization.Substring("Bearer ".Length).Trim(); } // If no token found, no further work possible if (string.IsNullOrEmpty(token)) { return(AuthenticateResult.Skip()); } } return(null); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var authorization = Request.Headers["authorization"]; if (string.IsNullOrEmpty(authorization)) { return(AuthenticateResult.Skip()); } var valid = Validate(Request); if (valid) { var principal = new ClaimsPrincipal(new ClaimsIdentity("HMAC")); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); return(AuthenticateResult.Success(ticket)); } return(AuthenticateResult.Fail("Authentication failed")); }
protected override Task <AuthenticateResult> HandleAuthenticateAsync() { var header = Request.Headers["Authorization"].ToString(); if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ")) { return(Task.FromResult(AuthenticateResult.Skip())); } var user = header.Substring(7); var principal = PrincipalFactory.Get(user); if (principal == null) { return(Task.FromResult(AuthenticateResult.Fail("No such user"))); } var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); return(Task.FromResult(AuthenticateResult.Success(ticket))); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { string authorization = Request.Headers["Authorization"]; if (string.IsNullOrEmpty(authorization)) { return(AuthenticateResult.Skip()); } string key = null; if (authorization.StartsWith("lousysecurity ", StringComparison.OrdinalIgnoreCase)) { key = authorization.Substring("lousysecurity ".Length).Trim(); } else { return(AuthenticateResult.Skip()); } try { var keyGuid = new Guid(key); var player = await Options.DbContext.Players.SingleOrDefaultAsync(x => x.LousySecurityKey == keyGuid); if (player != null) { var principal = new ClaimsPrincipal(new ClaimsIdentity(CreatePlayerClaims(player), Options.AuthenticationScheme)); return(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme))); } return(AuthenticateResult.Fail("No such user")); } catch (Exception ex) { return(AuthenticateResult.Fail(ex)); } }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { Logger.LogInformation("starting authentication handler for app service authentication"); if (this.Context.User == null || this.Context.User.Identity == null || this.Context.User.Identity.IsAuthenticated == false) { Logger.LogInformation("identity not found, attempting to fetch from auth endpoint /.auth/me"); var cookieContainer = new CookieContainer(); HttpClientHandler handler = new HttpClientHandler() { CookieContainer = cookieContainer }; var uriString = $"{Context.Request.Scheme}://{Context.Request.Host}"; Logger.LogDebug("host uri: {0}", uriString); foreach (var c in Context.Request.Cookies) { cookieContainer.Add(new Uri(uriString), new Cookie(c.Key, c.Value)); } Logger.LogDebug("found {0} cookies in request", cookieContainer.Count); foreach (var cookie in Context.Request.Cookies) { Logger.LogDebug(cookie.Key); } //fetch value from endpoint var request = new HttpRequestMessage(HttpMethod.Get, $"{uriString}/.auth/me"); foreach (var header in Context.Request.Headers) { if (header.Key.StartsWith("X-ZUMO-")) { request.Headers.Add(header.Key, header.Value[0]); } } JArray payload = null; using (HttpClient client = new HttpClient(handler)) { try { var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { Logger.LogDebug("auth endpoint was not sucessful. Status code: {0}, reason {1}", response.StatusCode, response.ReasonPhrase); return(AuthenticateResult.Fail("Unable to fetch user information from auth endpoint.")); } var content = await response.Content.ReadAsStringAsync(); payload = JArray.Parse(content); } catch (Exception ex) { Logger.LogError(ex.Message); } }; if (payload == null) { return(AuthenticateResult.Fail("Could not retreive json from /me endpoint.")); } //build up identity from json... var id = payload[0]["user_id"].Value <string>(); var idToken = payload[0]["id_token"].Value <string>(); var providerName = payload[0]["provider_name"].Value <string>(); Logger.LogDebug("payload was fetched from endpoint. id: {0}", id); var identity = new GenericIdentity(id); Logger.LogInformation("building claims from payload..."); List <Claim> claims = new List <Claim>(); foreach (var claim in payload[0]["user_claims"]) { claims.Add(new Claim(claim["typ"].ToString(), claim["val"].ToString())); } Logger.LogInformation("Add claims to new identity"); identity.AddClaims(claims); identity.AddClaim(new Claim("id_token", idToken)); identity.AddClaim(new Claim("provider_name", providerName)); ClaimsPrincipal p = new GenericPrincipal(identity, null); //todo add roles? var ticket = new AuthenticationTicket(p, new Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties(), Options.AuthenticationScheme); Logger.LogInformation("Set identity to user context object."); this.Context.User = p; Logger.LogInformation("identity build was a success, returning ticket"); return(AuthenticateResult.Success(ticket)); } Logger.LogInformation("identity already set, skipping middleware"); return(AuthenticateResult.Skip()); }
protected override async Task <AuthenticateResult> HandleRemoteAuthenticateAsync() { // Always extract the "state" parameter from the query string. var state = Request.Query[OpenIdAuthenticationConstants.Parameters.State]; if (string.IsNullOrEmpty(state)) { return(AuthenticateResult.Fail("The authentication response was rejected " + "because the state parameter was missing.")); } var properties = Options.StateDataFormat.Unprotect(state); if (properties == null) { return(AuthenticateResult.Fail("The authentication response was rejected " + "because the state parameter was invalid.")); } // Validate the anti-forgery token. if (!ValidateCorrelationId(properties)) { return(AuthenticateResult.Fail("The authentication response was rejected " + "because the anti-forgery token was invalid.")); } OpenIdAuthenticationMessage message; // OpenID 2.0 responses MUST necessarily be made using either GET or POST. // See http://openid.net/specs/openid-authentication-2_0.html#anchor4 if (!string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase) && !string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { return(AuthenticateResult.Fail("The authentication response was rejected because it was made " + "using an invalid method: make sure to use either GET or POST.")); } if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { message = new OpenIdAuthenticationMessage(Request.Query); } else { // OpenID 2.0 responses MUST include a Content-Type header when using POST. // See http://openid.net/specs/openid-authentication-2_0.html#anchor4 if (string.IsNullOrEmpty(Request.ContentType)) { return(AuthenticateResult.Fail("The authentication response was rejected because " + "it was missing the mandatory 'Content-Type' header.")); } // May have media/type; charset=utf-8, allow partial match. if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) { return(AuthenticateResult.Fail("The authentication response was rejected because an invalid Content-Type header " + "was received: make sure to use 'application/x-www-form-urlencoded'.")); } message = new OpenIdAuthenticationMessage(await Request.ReadFormAsync(Context.RequestAborted)); } // Ensure that the current request corresponds to an OpenID 2.0 assertion. if (!string.Equals(message.Namespace, OpenIdAuthenticationConstants.Namespaces.OpenId, StringComparison.Ordinal)) { return(AuthenticateResult.Fail("The authentication response was rejected because it was missing the mandatory " + "'openid.ns' parameter or because an unsupported version of OpenID was used.")); } // Stop processing the message if the authentication process was cancelled by the user. if (string.Equals(message.Mode, OpenIdAuthenticationConstants.Modes.Cancel, StringComparison.Ordinal)) { return(AuthenticateResult.Fail("The authentication response was rejected because " + "the operation was cancelled by the user.")); } // Stop processing the message if an error was returned by the provider. else if (string.Equals(message.Mode, OpenIdAuthenticationConstants.Modes.Error, StringComparison.Ordinal)) { if (string.IsNullOrEmpty(message.Error)) { return(AuthenticateResult.Fail("The authentication response was rejected because an " + "unspecified error was returned by the identity provider.")); } return(AuthenticateResult.Fail("The authentication response was rejected because " + $"an error was returned by the identity provider: {message.Error}.")); } // At this point, stop processing the message if the assertion was not positive. else if (!string.Equals(message.Mode, OpenIdAuthenticationConstants.Modes.IdRes, StringComparison.Ordinal)) { return(AuthenticateResult.Fail("The authentication response was rejected because " + "the identity provider declared it as invalid.")); } // Stop processing the message if the assertion // was not validated by the identity provider. if (!await VerifyAssertionAsync(message)) { return(AuthenticateResult.Fail("The authentication response was rejected by the identity provider.")); } var address = QueryHelpers.AddQueryString(uri: properties.Items[OpenIdAuthenticationConstants.Properties.ReturnTo], name: OpenIdAuthenticationConstants.Parameters.State, value: state); // Validate the return_to parameter by comparing it to the address stored in the properties. // See http://openid.net/specs/openid-authentication-2_0.html#verify_return_to if (!string.Equals(message.ReturnTo, address, StringComparison.Ordinal)) { return(AuthenticateResult.Fail("The authentication response was rejected because the return_to parameter was invalid.")); } // Make sure the OpenID 2.0 assertion contains an identifier. if (string.IsNullOrEmpty(message.ClaimedIdentifier)) { return(AuthenticateResult.Fail("The authentication response was rejected because it " + "was missing the mandatory 'claimed_id' parameter.")); } var identity = new ClaimsIdentity(Options.AuthenticationScheme); // Add the claimed identifier to the identity. identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, message.ClaimedIdentifier, ClaimValueTypes.String, Options.ClaimsIssuer)); // Add the most common attributes to the identity. var attributes = message.GetAttributes(); foreach (var attribute in attributes) { // http://axschema.org/contact/email if (string.Equals(attribute.Key, OpenIdAuthenticationConstants.Attributes.Email, StringComparison.Ordinal)) { identity.AddClaim(new Claim(ClaimTypes.Email, attribute.Value, ClaimValueTypes.Email, Options.ClaimsIssuer)); } // http://axschema.org/namePerson else if (string.Equals(attribute.Key, OpenIdAuthenticationConstants.Attributes.Name, StringComparison.Ordinal)) { identity.AddClaim(new Claim(ClaimTypes.Name, attribute.Value, ClaimValueTypes.String, Options.ClaimsIssuer)); } // http://axschema.org/namePerson/first else if (string.Equals(attribute.Key, OpenIdAuthenticationConstants.Attributes.Firstname, StringComparison.Ordinal)) { identity.AddClaim(new Claim(ClaimTypes.GivenName, attribute.Value, ClaimValueTypes.String, Options.ClaimsIssuer)); } // http://axschema.org/namePerson/last else if (string.Equals(attribute.Key, OpenIdAuthenticationConstants.Attributes.Lastname, StringComparison.Ordinal)) { identity.AddClaim(new Claim(ClaimTypes.Surname, attribute.Value, ClaimValueTypes.String, Options.ClaimsIssuer)); } } // Create a ClaimTypes.Name claim using ClaimTypes.GivenName and ClaimTypes.Surname // if the http://axschema.org/namePerson attribute cannot be found in the assertion. if (!identity.HasClaim(claim => string.Equals(claim.Type, ClaimTypes.Name, StringComparison.OrdinalIgnoreCase)) && identity.HasClaim(claim => string.Equals(claim.Type, ClaimTypes.GivenName, StringComparison.OrdinalIgnoreCase)) && identity.HasClaim(claim => string.Equals(claim.Type, ClaimTypes.Surname, StringComparison.OrdinalIgnoreCase))) { identity.AddClaim(new Claim(ClaimTypes.Name, $"{identity.FindFirst(ClaimTypes.GivenName).Value} " + $"{identity.FindFirst(ClaimTypes.Surname).Value}", ClaimValueTypes.String, Options.ClaimsIssuer)); } var ticket = await CreateTicketAsync(identity, properties, message.ClaimedIdentifier, attributes); if (ticket == null) { Logger.LogInformation("The authentication process was skipped because returned a null ticket was returned."); return(AuthenticateResult.Skip()); } return(AuthenticateResult.Success(ticket)); }
/// <summary> /// Searches for the 'X-ZUMO-AUTH' header for a token. If the tokne is found, it is validated using /// the options in the <see cref="AzureAppServiceAuthenticationOptions"/>. /// </summary> /// <returns></returns> protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { // Grab the X-ZUMO-AUTH token if it is available // If not, then try the Authorization Bearer token string token = Request.Headers["X-ZUMO-AUTH"]; if (string.IsNullOrEmpty(token)) { string authorization = Request.Headers["Authorization"]; if (string.IsNullOrEmpty(authorization)) { return(AuthenticateResult.Skip()); } if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = authorization.Substring("Bearer ".Length).Trim(); if (string.IsNullOrEmpty(token)) { return(AuthenticateResult.Skip()); } } } Logger.LogDebug($"Obtained Authorization Token = {token}"); // Convert the signing key we have to something we can use var signingKeys = new List <SecurityKey>(); // If the signingKey is the signature signingKeys.Add(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Options.SigningKey))); // If it's base-64 encoded try { signingKeys.Add(new SymmetricSecurityKey(Convert.FromBase64String(Options.SigningKey))); } catch (FormatException) { /* The key was not base 64 */ } // If it's hex encoded, then decode the hex and add it try { if (Options.SigningKey.Length % 2 == 0) { signingKeys.Add(new SymmetricSecurityKey( Enumerable.Range(0, Options.SigningKey.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(Options.SigningKey.Substring(x, 2), 16)) .ToArray() )); } } catch (Exception) { /* The key was not hex-encoded */ } // validation parameters var websiteAuthEnabled = Environment.GetEnvironmentVariable("WEBSITE_AUTH_ENABLED"); var inAzureAppService = (websiteAuthEnabled != null && websiteAuthEnabled.Equals("True", StringComparison.OrdinalIgnoreCase)); var tokenValidationParameters = new TokenValidationParameters { // The signature must have been created by the signing key ValidateIssuerSigningKey = !inAzureAppService, IssuerSigningKeys = signingKeys, // The Issuer (iss) claim must match ValidateIssuer = true, ValidIssuers = Options.AllowedIssuers, // The Audience (aud) claim must match ValidateAudience = true, ValidAudiences = Options.AllowedAudiences, // Validate the token expiry ValidateLifetime = true, // If you want to allow clock drift, set that here ClockSkew = TimeSpan.FromSeconds(60) }; // validate the token we received var tokenHandler = new JwtSecurityTokenHandler(); SecurityToken validatedToken; ClaimsPrincipal principal; try { principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out validatedToken); } catch (Exception ex) { Logger.LogError(101, ex, "Cannot validate JWT"); return(AuthenticateResult.Fail(ex)); } // Get the actual claims from the {issuer}/.auth/me try { client.BaseAddress = new Uri(validatedToken.Issuer); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Add("X-ZUMO-AUTH", token); HttpResponseMessage response = await client.GetAsync("/.auth/me"); if (response.IsSuccessStatusCode) { var jsonContent = await response.Content.ReadAsStringAsync(); var userRecord = JsonConvert.DeserializeObject <List <AzureAppServiceClaims> >(jsonContent).First(); // Create a new ClaimsPrincipal based on the results of /.auth/me List <Claim> claims = new List <Claim>(); foreach (var claim in userRecord.UserClaims) { claims.Add(new Claim(claim.Type, claim.Value)); } claims.Add(new Claim("x-auth-provider-name", userRecord.ProviderName)); claims.Add(new Claim("x-auth-provider-token", userRecord.IdToken)); claims.Add(new Claim("x-user-id", userRecord.UserId)); var identity = new GenericIdentity(principal.Claims.Where(x => x.Type.Equals("stable_sid")).First().Value, Options.AuthenticationScheme); identity.AddClaims(claims); principal = new ClaimsPrincipal(identity); } else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { return(AuthenticateResult.Fail("/.auth/me says you are unauthorized")); } else { Logger.LogWarning($"/.auth/me returned status = {response.StatusCode} - skipping user claims population"); } } catch (Exception ex) { Logger.LogWarning($"Unable to get /.auth/me user claims - skipping (ex = {ex.GetType().FullName}, msg = {ex.Message})"); } // Generate a new authentication ticket and return success var ticket = new AuthenticationTicket( principal, new AuthenticationProperties(), Options.AuthenticationScheme); return(AuthenticateResult.Success(ticket)); }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { string token = null; AuthenticateResult result = null; try { // Give application opportunity to find from a different location, adjust, or reject token var messageReceivedContext = new MessageReceivedContext(Context, Options); // event can set the token await Options.Events.MessageReceived(messageReceivedContext); if (messageReceivedContext.CheckEventResult(out result)) { return(result); } // If application retrieved token from somewhere else, use that. token = messageReceivedContext.Token; if (string.IsNullOrEmpty(token)) { string authorization = Request.Headers["Authorization"]; // If no authorization header found, nothing to process further if (string.IsNullOrEmpty(authorization)) { return(AuthenticateResult.Skip()); } if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = authorization.Substring("Bearer ".Length).Trim(); } // If no token found, no further work possible if (string.IsNullOrEmpty(token)) { return(AuthenticateResult.Skip()); } } var validationParameters = await Param(); List <Exception> validationFailures = null; SecurityToken validatedToken; foreach (var validator in Options.SecurityTokenValidators) { if (validator.CanReadToken(token)) { ClaimsPrincipal principal; try { principal = validator.ValidateToken(token, validationParameters, out validatedToken); } catch (Exception ex) { // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event. if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null && ex is SecurityTokenSignatureKeyNotFoundException) { Options.ConfigurationManager.RequestRefresh(); } if (validationFailures == null) { validationFailures = new List <Exception>(1); } validationFailures.Add(ex); continue; } var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); var tokenValidatedContext = new TokenValidatedContext(Context, Options) { Ticket = ticket, SecurityToken = validatedToken, }; await Options.Events.TokenValidated(tokenValidatedContext); if (tokenValidatedContext.CheckEventResult(out result)) { return(result); } ticket = tokenValidatedContext.Ticket; string email = ticket.Principal.Identity.Name; var user = UserExtensions.GetUser(ticket.Principal.Identity.Name); if (user.User.Status != UserStatus.Active) { return(AuthenticateResult.Skip()); } if (Options.SaveToken) { ticket.Properties.StoreTokens(new[] { new AuthenticationToken { Name = "access_token", Value = token } }); } return(AuthenticateResult.Success(ticket)); } } if (validationFailures != null) { var authenticationFailedContext = new AuthenticationFailedContext(Context, Options) { Exception = (validationFailures.Count == 1) ? validationFailures[0] : new AggregateException(validationFailures) }; await Options.Events.AuthenticationFailed(authenticationFailedContext); if (authenticationFailedContext.CheckEventResult(out result)) { return(result); } return(AuthenticateResult.Fail(authenticationFailedContext.Exception)); } return(AuthenticateResult.Fail("No SecurityTokenValidator available for token: " + token ?? "[null]")); } catch (Exception ex) { var authenticationFailedContext = new AuthenticationFailedContext(Context, Options) { Exception = ex }; await Options.Events.AuthenticationFailed(authenticationFailedContext); if (authenticationFailedContext.CheckEventResult(out result)) { return(result); } throw; } }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { string token = Options.TokenRetriever(Context.Request); if (token.IsMissing()) { return(AuthenticateResult.Skip()); } if (token.Contains('.') && Options.SkipTokensWithDots) { _logger.LogTrace("Token contains a dot - skipped because SkipTokensWithDots is set."); return(AuthenticateResult.Skip()); } // Resolve request generator _requestGenerator = Context.RequestServices.GetService <IIntrospectionRequestGenerator>() ?? new DefaultIntrospectionRequestGenerator(); if (Options.EnableCaching) { var claims = await _cache.GetClaimsAsync(token).ConfigureAwait(false); if (claims != null && await _requestGenerator.UseCacheAsync(Context.Request, claims)) { var ticket = CreateTicket(claims); _logger.LogTrace("Token found in cache."); if (Options.SaveToken) { ticket.Properties.StoreTokens(new[] { new AuthenticationToken { Name = "access_token", Value = token } }); } return(AuthenticateResult.Success(ticket)); } _logger.LogTrace("Token is not cached, or the introspection response generator rejected the currently cached claims."); } // Use a LazyAsync to ensure only one thread is requesting introspection for a token - the rest will wait for the result var lazyIntrospection = _lazyTokenIntrospections.GetOrAdd(token, CreateLazyIntrospection); try { var response = await lazyIntrospection.Value.ConfigureAwait(false); if (response.IsError) { _logger.LogError("Error returned from introspection endpoint: " + response.Error); return(AuthenticateResult.Fail("Error returned from introspection endpoint: " + response.Error)); } if (response.IsActive) { var ticket = CreateTicket(response.Claims); if (Options.SaveToken) { ticket.Properties.StoreTokens(new[] { new AuthenticationToken { Name = "access_token", Value = token } }); } if (Options.EnableCaching) { await _cache.SetClaimsAsync(token, response.Claims, Options.CacheDuration, _logger).ConfigureAwait(false); } return(AuthenticateResult.Success(ticket)); } else { return(AuthenticateResult.Fail("Token is not active.")); } } finally { // If caching is on and it succeeded, the claims are now in the cache. // If caching is off and it succeeded, the claims will be discarded. // Either way, we want to remove the temporary store of claims for this token because it is only intended for de-duping fetch requests AsyncLazy <IntrospectionResponse> removed; _lazyTokenIntrospections.TryRemove(token, out removed); } }
// private OpenIdConnectConfiguration _configuration; /// <summary> /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options. /// </summary> /// <returns></returns> protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { string url = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(Context.Request); System.Console.WriteLine(url); string token = null; AuthenticateResult result = null; try { // Give application opportunity to find from a different location, adjust, or reject token var messageReceivedContext = new MessageReceivedContext(Context, Options); // event can set the token // await Options.Events.MessageReceived(messageReceivedContext); if (messageReceivedContext.CheckEventResult(out result)) { return(result); } // If application retrieved token from somewhere else, use that. token = messageReceivedContext.Token; if (string.IsNullOrEmpty(token)) { string authorization = Request.Headers["Authorization"]; // If no authorization header found, nothing to process further if (string.IsNullOrEmpty(authorization)) { return(AuthenticateResult.Skip()); } if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = authorization.Substring("Bearer ".Length).Trim(); } // If no token found, no further work possible if (string.IsNullOrEmpty(token)) { return(AuthenticateResult.Skip()); } } /* * if (_configuration == null && Options.ConfigurationManager != null) * { * _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); * } * * var validationParameters = Options.TokenValidationParameters.Clone(); * if (_configuration != null) * { * if (validationParameters.ValidIssuer == null && !string.IsNullOrEmpty(_configuration.Issuer)) * { * validationParameters.ValidIssuer = _configuration.Issuer; * } * else * { * var issuers = new[] { _configuration.Issuer }; * validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? issuers : validationParameters.ValidIssuers.Concat(issuers)); * } * * validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys)); * } */ /* * List<Exception> validationFailures = null; * SecurityToken validatedToken; * foreach (var validator in Options.SecurityTokenValidators) * { * if (validator.CanReadToken(token)) * { * ClaimsPrincipal principal; * try * { * principal = validator.ValidateToken(token, validationParameters, out validatedToken); * } * catch (Exception ex) * { * Logger.TokenValidationFailed(token, ex); * * // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event. * if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null * && ex is SecurityTokenSignatureKeyNotFoundException) * { * Options.ConfigurationManager.RequestRefresh(); * } * * if (validationFailures == null) * { * validationFailures = new List<Exception>(1); * } * validationFailures.Add(ex); * continue; * } * * Logger.TokenValidationSucceeded(); * * var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); * var tokenValidatedContext = new TokenValidatedContext(Context, Options) * { * Ticket = ticket, * SecurityToken = validatedToken, * }; * * await Options.Events.TokenValidated(tokenValidatedContext); * if (tokenValidatedContext.CheckEventResult(out result)) * { * return result; * } * ticket = tokenValidatedContext.Ticket; * * if (Options.SaveToken) * { * ticket.Properties.StoreTokens(new[] * { * new AuthenticationToken { Name = "access_token", Value = token } * }); * } * * return AuthenticateResult.Success(ticket); * } * } */ /* * if (validationFailures != null) * { * var authenticationFailedContext = new AuthenticationFailedContext(Context, Options) * { * Exception = (validationFailures.Count == 1) ? validationFailures[0] : new AggregateException(validationFailures) * }; * * await Options.Events.AuthenticationFailed(authenticationFailedContext); * if (authenticationFailedContext.CheckEventResult(out result)) * { * return result; * } * * return AuthenticateResult.Fail(authenticationFailedContext.Exception); * } */ return(AuthenticateResult.Fail("No SecurityTokenValidator available for token: " + token ?? "[null]")); } catch (Exception ex) { // Logger.ErrorProcessingMessage(ex); var authenticationFailedContext = new AuthenticationFailedContext(Context, Options) { Exception = ex }; // await Options.Events.AuthenticationFailed(authenticationFailedContext); if (authenticationFailedContext.CheckEventResult(out result)) { return(result); } throw; } }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var notification = new MatchEndpointContext(Context, Options); if (Options.AuthorizationEndpointPath.HasValue && Options.AuthorizationEndpointPath == Request.Path) { notification.MatchesAuthorizationEndpoint(); } else if (Options.LogoutEndpointPath.HasValue && Options.LogoutEndpointPath == Request.Path) { notification.MatchesLogoutEndpoint(); } else if (Options.UserinfoEndpointPath.HasValue && Options.UserinfoEndpointPath == Request.Path) { notification.MatchesUserinfoEndpoint(); } await Options.Provider.MatchEndpoint(notification); if (!notification.IsAuthorizationEndpoint && !notification.IsLogoutEndpoint && !notification.IsUserinfoEndpoint) { return(AuthenticateResult.Skip()); } // Try to retrieve the current OpenID Connect request from the ASP.NET context. // If the request cannot be found, this means that this middleware was configured // to use the automatic authentication mode and that HandleAuthenticateAsync // was invoked before Invoke*EndpointAsync: in this case, the OpenID Connect // request is directly extracted from the query string or the request form. var request = Context.GetOpenIdConnectRequest(); if (request == null) { if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { request = new OpenIdConnectMessage(Request.Query.ToDictionary()); } else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(Request.ContentType)) { return(AuthenticateResult.Skip()); } else if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) { return(AuthenticateResult.Skip()); } var form = await Request.ReadFormAsync(Context.RequestAborted); request = new OpenIdConnectMessage(form.ToDictionary()); } } // Missing or invalid requests are ignored in HandleAuthenticateAsync: // in this case, Skip is used to indicate authentication failed. if (request == null) { return(AuthenticateResult.Skip()); } if (notification.IsAuthorizationEndpoint || notification.IsLogoutEndpoint) { if (string.IsNullOrEmpty(request.IdTokenHint)) { return(AuthenticateResult.Skip()); } var ticket = await DeserializeIdentityTokenAsync(request.IdTokenHint, request); if (ticket == null) { Logger.LogWarning("The identity token extracted from the id_token_hint " + "parameter was invalid and has been ignored."); return(AuthenticateResult.Skip()); } // Tickets are returned even if they // are considered invalid (e.g expired). return(AuthenticateResult.Success(ticket)); } else if (notification.IsUserinfoEndpoint) { string token; if (!string.IsNullOrEmpty(request.AccessToken)) { token = request.AccessToken; } else { string header = Request.Headers[HeaderNames.Authorization]; if (string.IsNullOrEmpty(header)) { return(AuthenticateResult.Skip()); } if (!header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { return(AuthenticateResult.Skip()); } token = header.Substring("Bearer ".Length); if (string.IsNullOrWhiteSpace(token)) { return(AuthenticateResult.Skip()); } } var ticket = await DeserializeAccessTokenAsync(token, request); if (ticket == null) { Logger.LogWarning("The access token extracted from the userinfo " + "request was expired and has been ignored."); return(AuthenticateResult.Skip()); } if (!ticket.Properties.ExpiresUtc.HasValue || ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow) { Logger.LogWarning("The access token extracted from the userinfo " + "request was expired and has been ignored."); return(AuthenticateResult.Skip()); } return(AuthenticateResult.Success(ticket)); } return(AuthenticateResult.Skip()); }
/// <summary> /// Invoked to process incoming OpenIdConnect messages. /// </summary> /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns> protected override async Task <AuthenticateResult> HandleRemoteAuthenticateAsync() { Logger.EnteringOpenIdAuthenticationHandlerHandleRemoteAuthenticateAsync(GetType().FullName); OpenIdConnectMessage authorizationResponse = null; if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { authorizationResponse = new OpenIdConnectMessage(Request.Query.Select(pair => new KeyValuePair <string, string[]>(pair.Key, pair.Value))); // response_mode=query (explicit or not) and a response_type containing id_token // or token are not considered as a safe combination and MUST be rejected. // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security if (!string.IsNullOrEmpty(authorizationResponse.IdToken) || !string.IsNullOrEmpty(authorizationResponse.AccessToken)) { if (Options.SkipUnrecognizedRequests) { // Not for us? return(AuthenticateResult.Skip()); } return(AuthenticateResult.Fail("An OpenID Connect response cannot contain an " + "identity token or an access token when using response_mode=query")); } } // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { var form = await Request.ReadFormAsync(); authorizationResponse = new OpenIdConnectMessage(form.Select(pair => new KeyValuePair <string, string[]>(pair.Key, pair.Value))); } if (authorizationResponse == null) { if (Options.SkipUnrecognizedRequests) { // Not for us? return(AuthenticateResult.Skip()); } return(AuthenticateResult.Fail("No message.")); } AuthenticateResult result; try { AuthenticationProperties properties = null; if (!string.IsNullOrEmpty(authorizationResponse.State)) { properties = Options.StateDataFormat.Unprotect(authorizationResponse.State); } var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse, properties); if (messageReceivedContext.CheckEventResult(out result)) { return(result); } authorizationResponse = messageReceivedContext.ProtocolMessage; properties = messageReceivedContext.Properties; if (properties == null) { // Fail if state is missing, it's required for the correlation id. if (string.IsNullOrEmpty(authorizationResponse.State)) { // This wasn't a valid OIDC message, it may not have been intended for us. Logger.NullOrEmptyAuthorizationResponseState(); if (Options.SkipUnrecognizedRequests) { return(AuthenticateResult.Skip()); } return(AuthenticateResult.Fail(Resources.MessageStateIsNullOrEmpty)); } // if state exists and we failed to 'unprotect' this is not a message we should process. properties = Options.StateDataFormat.Unprotect(authorizationResponse.State); } if (properties == null) { Logger.UnableToReadAuthorizationResponseState(); if (Options.SkipUnrecognizedRequests) { // Not for us? return(AuthenticateResult.Skip()); } return(AuthenticateResult.Fail(Resources.MessageStateIsInvalid)); } string userstate = null; properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate); authorizationResponse.State = userstate; if (!ValidateCorrelationId(properties)) { return(AuthenticateResult.Fail("Correlation failed.")); } // if any of the error fields are set, throw error null if (!string.IsNullOrEmpty(authorizationResponse.Error)) { Logger.AuthorizationResponseError( authorizationResponse.Error, authorizationResponse.ErrorDescription ?? "ErrorDecription null", authorizationResponse.ErrorUri ?? "ErrorUri null"); return(AuthenticateResult.Fail(new OpenIdConnectProtocolException( string.Format(CultureInfo.InvariantCulture, Resources.MessageContainsError, authorizationResponse.Error, authorizationResponse.ErrorDescription ?? "ErrorDecription null", authorizationResponse.ErrorUri ?? "ErrorUri null")))); } if (_configuration == null && Options.ConfigurationManager != null) { Logger.UpdatingConfiguration(); _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } PopulateSessionProperties(authorizationResponse, properties); AuthenticationTicket ticket = null; JwtSecurityToken jwt = null; string nonce = null; var validationParameters = Options.TokenValidationParameters.Clone(); // Hybrid or Implicit flow if (!string.IsNullOrEmpty(authorizationResponse.IdToken)) { Logger.ReceivedIdToken(); ticket = ValidateToken(authorizationResponse.IdToken, properties, validationParameters, out jwt); nonce = jwt?.Payload.Nonce; if (!string.IsNullOrEmpty(nonce)) { nonce = ReadNonceCookie(nonce); } var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, null, properties, ticket, jwt, nonce); if (tokenValidatedContext.CheckEventResult(out result)) { return(result); } authorizationResponse = tokenValidatedContext.ProtocolMessage; properties = tokenValidatedContext.Properties; ticket = tokenValidatedContext.Ticket; jwt = tokenValidatedContext.SecurityToken; nonce = tokenValidatedContext.Nonce; } Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() { ClientId = Options.ClientId, ProtocolMessage = authorizationResponse, ValidatedIdToken = jwt, Nonce = nonce }); OpenIdConnectMessage tokenEndpointResponse = null; // Authorization Code or Hybrid flow if (!string.IsNullOrEmpty(authorizationResponse.Code)) { var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(authorizationResponse, properties, ticket, jwt); if (authorizationCodeReceivedContext.CheckEventResult(out result)) { return(result); } authorizationResponse = authorizationCodeReceivedContext.ProtocolMessage; properties = authorizationCodeReceivedContext.Properties; var tokenEndpointRequest = authorizationCodeReceivedContext.TokenEndpointRequest; // If the developer redeemed the code themselves... tokenEndpointResponse = authorizationCodeReceivedContext.TokenEndpointResponse; ticket = authorizationCodeReceivedContext.Ticket; jwt = authorizationCodeReceivedContext.JwtSecurityToken; if (!authorizationCodeReceivedContext.HandledCodeRedemption) { tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest); } var tokenResponseReceivedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, properties); if (tokenResponseReceivedContext.CheckEventResult(out result)) { return(result); } authorizationResponse = tokenResponseReceivedContext.ProtocolMessage; tokenEndpointResponse = tokenResponseReceivedContext.TokenEndpointResponse; // We only have to process the IdToken if we didn't already get one in the AuthorizationResponse if (ticket == null) { // no need to validate signature when token is received using "code flow" as per spec // [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation]. validationParameters.RequireSignedTokens = false; ticket = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out jwt); nonce = jwt?.Payload.Nonce; if (!string.IsNullOrEmpty(nonce)) { nonce = ReadNonceCookie(nonce); } var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, tokenEndpointResponse, properties, ticket, jwt, nonce); if (tokenValidatedContext.CheckEventResult(out result)) { return(result); } authorizationResponse = tokenValidatedContext.ProtocolMessage; tokenEndpointResponse = tokenValidatedContext.TokenEndpointResponse; properties = tokenValidatedContext.Properties; ticket = tokenValidatedContext.Ticket; jwt = tokenValidatedContext.SecurityToken; nonce = tokenValidatedContext.Nonce; } // Validate the token response if it wasn't provided manually if (!authorizationCodeReceivedContext.HandledCodeRedemption) { Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext() { ClientId = Options.ClientId, ProtocolMessage = tokenEndpointResponse, ValidatedIdToken = jwt, Nonce = nonce }); } } if (Options.SaveTokens) { SaveTokens(ticket.Properties, tokenEndpointResponse ?? authorizationResponse); } if (Options.GetClaimsFromUserInfoEndpoint) { return(await GetUserInformationAsync(tokenEndpointResponse ?? authorizationResponse, jwt, ticket)); } return(AuthenticateResult.Success(ticket)); } catch (Exception exception) { Logger.ExceptionProcessingMessage(exception); // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event. if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { if (Options.ConfigurationManager != null) { Logger.ConfigurationManagerRequestRefreshCalled(); Options.ConfigurationManager.RequestRefresh(); } } var authenticationFailedContext = await RunAuthenticationFailedEventAsync(authorizationResponse, exception); if (authenticationFailedContext.CheckEventResult(out result)) { return(result); } return(AuthenticateResult.Fail(exception)); } }
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var request = Context.GetOpenIdConnectRequest(); if (request == null) { throw new InvalidOperationException("An identity cannot be extracted from this request."); } if (request.IsAuthorizationRequest() || request.IsLogoutRequest()) { if (string.IsNullOrEmpty(request.IdTokenHint)) { return(AuthenticateResult.Skip()); } var ticket = await DeserializeIdentityTokenAsync(request.IdTokenHint, request); if (ticket == null) { Logger.LogWarning("The identity token extracted from the id_token_hint " + "parameter was invalid and has been ignored."); return(AuthenticateResult.Skip()); } // Tickets are returned even if they // are considered invalid (e.g expired). return(AuthenticateResult.Success(ticket)); } else if (request.IsTokenRequest()) { // Note: this method can be called from the ApplyTokenResponse event, // which may be invoked for a missing authorization code/refresh token. if (request.IsAuthorizationCodeGrantType()) { if (string.IsNullOrEmpty(request.Code)) { return(AuthenticateResult.Skip()); } var ticket = await DeserializeAuthorizationCodeAsync(request.Code, request); if (ticket == null) { Logger.LogWarning("The authorization code extracted from the " + "token request was invalid and has been ignored."); return(AuthenticateResult.Skip()); } return(AuthenticateResult.Success(ticket)); } else if (request.IsRefreshTokenGrantType()) { if (string.IsNullOrEmpty(request.RefreshToken)) { return(AuthenticateResult.Skip()); } var ticket = await DeserializeRefreshTokenAsync(request.RefreshToken, request); if (ticket == null) { Logger.LogWarning("The refresh token extracted from the " + "token request was invalid and has been ignored."); return(AuthenticateResult.Skip()); } return(AuthenticateResult.Success(ticket)); } return(AuthenticateResult.Skip()); } throw new InvalidOperationException("An identity cannot be extracted from this request."); }
/// <summary> /// Authenticate the user identity with the identity provider. /// The method process the request on the endpoint defined by CallbackPath. /// </summary> protected override async Task <AuthenticateResult> HandleRemoteAuthenticateAsync() { // Allow login to be constrained to a specific path. if (Options.CallbackPath.HasValue && !Options.CallbackPath.Equals(Request.PathBase + Request.Path, StringComparison.OrdinalIgnoreCase)) { // Not for us. return(AuthenticateResult.Skip()); } WsFederationMessage wsFederationMessage = null; // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { Logger.LogDebug("Buffering request body"); // Buffer in case this body was not meant for us. var memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } var form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); // TODO: a delegate on WsFederationAuthenticationOptions would allow for users to hook their own custom message. wsFederationMessage = new WsFederationMessage( form.Select(pair => new KeyValuePair <string, string[]>(pair.Key, pair.Value.ToArray()))); } if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage) { if (Options.SkipUnrecognizedRequests) { // Not for us? return(AuthenticateResult.Skip()); } return(AuthenticateResult.Fail("No message")); } try { var messageReceivedContext = await RunMessageReceivedEventAsync(wsFederationMessage); AuthenticateResult result; if (messageReceivedContext.CheckEventResult(out result)) { return(result); } if (wsFederationMessage.Wresult == null) { return(AuthenticateResult.Fail("Received a sign-in message without a WResult.")); } var token = wsFederationMessage.GetToken(); if (string.IsNullOrWhiteSpace(token)) { return(AuthenticateResult.Fail("Received a sign-in message without a token.")); } var securityTokenContext = await RunSecurityTokenReceivedEventAsync(wsFederationMessage); if (securityTokenContext.CheckEventResult(out result)) { return(result); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } // Copy and augment to avoid cross request race conditions for updated configurations. var tvp = Options.TokenValidationParameters.Clone(); IEnumerable <string> issuers = new[] { _configuration.Issuer }; tvp.ValidIssuers = tvp.ValidIssuers?.Concat(issuers) ?? issuers; tvp.IssuerSigningKeys = tvp.IssuerSigningKeys?.Concat(_configuration.SigningKeys) ?? _configuration.SigningKeys; SecurityToken parsedToken; var principal = Options.SecurityTokenHandlers.ValidateToken(token, tvp, out parsedToken); if (!string.IsNullOrEmpty(Options.BootStrapTokenClaimName) && parsedToken != null) { ClaimsIdentity identity = principal.Identity as ClaimsIdentity; if (identity != null) { StringBuilder sb = new StringBuilder(); var writer = XmlWriter.Create(new StringWriter(sb), new XmlWriterSettings { OmitXmlDeclaration = true }); Options.SecurityTokenHandlers[parsedToken].WriteToken(writer, parsedToken); writer.Flush(); identity.AddClaim(new Claim(Options.BootStrapTokenClaimName, Convert.ToBase64String(Encoding.UTF8.GetBytes(sb.ToString())))); } } // Retrieve our cached redirect uri var state = wsFederationMessage.Wctx; // WsFed allows for uninitiated logins, state may be missing. var properties = GetPropertiesFromWctx(state); var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme); if (Options.UseTokenLifetime) { // Override any session persistence to match the token lifetime. var issued = parsedToken.ValidFrom; if (issued != DateTime.MinValue) { ticket.Properties.IssuedUtc = issued.ToUniversalTime(); } var expires = parsedToken.ValidTo; if (expires != DateTime.MinValue) { ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); } ticket.Properties.AllowRefresh = false; } var securityTokenValidatedNotification = await RunSecurityTokenValidatedEventAsync(wsFederationMessage, ticket); return(securityTokenValidatedNotification.CheckEventResult(out result) ? result : AuthenticateResult.Success(ticket)); } catch (Exception exception) { _logger.LogError("Exception occurred while processing message: ", exception); // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification. if (Options.RefreshOnIssuerKeyNotFound && exception.GetType() == typeof(SecurityTokenSignatureKeyNotFoundException)) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = await RunAuthenticationFailedEventAsync(wsFederationMessage, exception); return(authenticationFailedNotification.CheckEventResult(out AuthenticateResult result) ? result : AuthenticateResult.Fail(exception)); } }