Last active
November 14, 2017 17:34
-
-
Save chrisjmccrum/3e3bf3cc7c2f32d89ecb323653f2f603 to your computer and use it in GitHub Desktop.
AuthenticationHelper for WebApp Cookie Auth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Globalization; | |
| using System.IdentityModel.Tokens.Jwt; | |
| using System.Linq; | |
| using System.Security.Claims; | |
| using System.Threading.Tasks; | |
| using AspNet.Security.OpenIdConnect.Client; | |
| using AspNet.Security.OpenIdConnect.Primitives; | |
| using Microsoft.AspNetCore.Authentication; | |
| using Microsoft.AspNetCore.Authentication.Cookies; | |
| using Microsoft.Extensions.Configuration; | |
| namespace ASPNET_Core_1_0 | |
| { | |
| public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents | |
| { | |
| private readonly OpenIdConnectClient _client; | |
| private readonly IConfiguration _config; | |
| public CustomCookieAuthenticationEvents( | |
| OpenIdConnectClient client, | |
| IConfiguration config | |
| ) | |
| { | |
| _client = client; | |
| _config = config; | |
| } | |
| public override async Task ValidatePrincipal(CookieValidatePrincipalContext context) | |
| { | |
| if (context == null) | |
| { | |
| throw new ArgumentNullException(nameof(context)); | |
| } | |
| // Try to resolve the expiration date from the authentication properties. | |
| var expiration = context.Properties.GetTokenValue("expires_at"); | |
| if (string.IsNullOrEmpty(expiration)) | |
| { | |
| return; | |
| } | |
| // If the token is still valid, skip the additional validation logic. | |
| if (DateTimeOffset.UtcNow < (DateTimeOffset.Parse(expiration) - TimeSpan.FromMinutes(5))) | |
| { | |
| return; | |
| } | |
| var token = context.Properties.GetTokenValue(OpenIdConnectConstants.Parameters.RefreshToken); | |
| if (string.IsNullOrEmpty(token)) | |
| { | |
| return; | |
| } | |
| var request = new OpenIdConnectRequest | |
| { | |
| ClientId = _config["OpenId:ClientId"], | |
| ClientSecret = _config["OpenId:ClientSecret"], | |
| GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken, | |
| RefreshToken = token | |
| }; | |
| var response = await _client.PostAsync(_config["OpenId:TokenEndpoint"], request); | |
| // If an error was returned by the authorization server, invalidate the cookie. | |
| if (!string.IsNullOrEmpty(response.Error) || string.IsNullOrEmpty(response.AccessToken) || | |
| string.IsNullOrEmpty(response.IdToken)) | |
| { | |
| context.RejectPrincipal(); | |
| await context.HttpContext.SignOutAsync(context.Scheme.Name); | |
| return; | |
| } | |
| var tokens = GetTokens(response).ToList(); | |
| // If no refresh token was returned in the token response, keep using the old token. | |
| if (!tokens.Any(item => item.Value == OpenIdConnectConstants.Parameters.RefreshToken)) | |
| { | |
| tokens.Add(new AuthenticationToken | |
| { | |
| Name = OpenIdConnectConstants.Parameters.RefreshToken, | |
| Value = token | |
| }); | |
| } | |
| context.Properties.StoreTokens(tokens: tokens); | |
| context.ReplacePrincipal(principal: GetPrincipal(response)); | |
| context.ShouldRenew = true; | |
| } | |
| public ClaimsPrincipal GetPrincipal(OpenIdConnectResponse response) | |
| { | |
| // Validating the identity token is not necessary, | |
| // as it's retrieved using backchannel communication. | |
| var token = new JwtSecurityToken(response.IdToken); | |
| // Create a new identity containing the claims retrieved from the identity token. | |
| var identity = new ClaimsIdentity(token.Claims, CookieAuthenticationDefaults.AuthenticationScheme); | |
| identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, token.Payload.Sub)); | |
| // identity.AddClaim(new Claim(ClaimTypes.Name, (string) token.Payload["unique_name"])); | |
| return new ClaimsPrincipal(identity); | |
| } | |
| public IEnumerable<AuthenticationToken> GetTokens(OpenIdConnectResponse response) | |
| { | |
| if (response == null) | |
| { | |
| throw new ArgumentNullException(nameof(response)); | |
| } | |
| yield return new AuthenticationToken | |
| { | |
| Name = OpenIdConnectConstants.Parameters.AccessToken, | |
| Value = response.AccessToken | |
| }; | |
| // Note: the refresh_token is optional. | |
| if (!string.IsNullOrEmpty(response.RefreshToken)) | |
| { | |
| yield return new AuthenticationToken | |
| { | |
| Name = OpenIdConnectConstants.Parameters.RefreshToken, | |
| Value = response.RefreshToken | |
| }; | |
| } | |
| // Note: the expires_in parameter is optional. | |
| if (response.ExpiresIn != null) | |
| { | |
| yield return new AuthenticationToken | |
| { | |
| Name = "expires_at", | |
| Value = (DateTimeOffset.UtcNow + TimeSpan.FromSeconds(response.ExpiresIn.Value)).ToString("o", CultureInfo.InvariantCulture) | |
| }; | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment