Ejemplo n.º 1
0
        public Task StoreDownstreamIdTokenResponseAsync(string id, DownstreamAuthorizeResponse response)
        {
            var key = OIDCPipleLineStoreUtils.GenerateDownstreamIdTokenResponseKey(id);

            _memoryCache.Set(
                key,
                response,
                TimeSpan.FromMinutes(_options.ExpirationMinutes));
            return(Task.CompletedTask);
        }
        public async Task OnGetAsync()
        {
            if (User.Identity.IsAuthenticated)
            {
                string nonce = _oidcPipeLineKey.GetOIDCPipeLineKey();
                IdTokenResponse = await _oidcPipelineStore.GetDownstreamIdTokenResponseAsync(nonce);

                Claims = Request.HttpContext.User.Claims.ToList();
            }
        }
 public async Task StoreDownstreamIdTokenResponseAsync(string id, DownstreamAuthorizeResponse response)
 {
     var key  = OIDCPipleLineStoreUtils.GenerateDownstreamIdTokenResponseKey(id);
     var data = _binarySerializer.Serialize(response);
     DistributedCacheEntryOptions options = new DistributedCacheEntryOptions
     {
         AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_options.ExpirationMinutes)
     };
     await _cache.SetAsync(key, data, options);
 }
        public static IdentityBuilder AddAuthentication <TUser>(this IServiceCollection services,
                                                                IConfiguration configuration,
                                                                Action <IdentityOptions> setupAction)
            where TUser : class
        {
            // Services used by identity

            var authenticationBuilder = services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
                options.DefaultChallengeScheme    = IdentityConstants.ApplicationScheme;
                options.DefaultSignInScheme       = IdentityConstants.ExternalScheme;
            });
            // doesn't work here either.
            //----------------------------------

            /*
             * services.AddOptions<OpenIdConnectOptions>()
             *         .Configure<IOIDCPipelineStore, IHttpContextAccessor>((options, oidcPipelineStore, accessor) =>
             *         {
             *             options.ProtocolValidator = new MyOpenIdConnectProtocolValidator(oidcPipelineStore, accessor)
             *             {
             *                 RequireTimeStampInNonce = false,
             *                 RequireStateValidation = false,
             *                 RequireNonce = true,
             *                 NonceLifetime = TimeSpan.FromMinutes(15)
             *             };
             *         });
             */
            var section = configuration.GetSection("openIdConnect");
            var openIdConnectSchemeRecordSchemeRecords = new List <OpenIdConnectSchemeRecord>();

            section.Bind(openIdConnectSchemeRecordSchemeRecords);
            foreach (var record in openIdConnectSchemeRecordSchemeRecords)
            {
                var scheme = record.Scheme;
                services.AddOptions <OpenIdConnectOptions>(scheme)
                .Configure <IOIDCPipeLineKey, IOIDCPipelineStore>(
                    (options, oidcPipeLineKey, oidcPipelineStore) =>
                {
                    options.ProtocolValidator = new OIDCPipelineOpenIdConnectProtocolValidator(oidcPipeLineKey, oidcPipelineStore)
                    {
                        RequireTimeStampInNonce = false,
                        RequireStateValidation  = false,
                        RequireNonce            = true,
                        NonceLifetime           = TimeSpan.FromMinutes(15)
                    };
                });


                authenticationBuilder.AddOpenIdConnect(scheme, scheme, options =>
                {
                    options.Authority            = record.Authority;
                    options.CallbackPath         = record.CallbackPath;
                    options.RequireHttpsMetadata = false;
                    if (!string.IsNullOrEmpty(record.ResponseType))
                    {
                        options.ResponseType = record.ResponseType;
                    }
                    options.GetClaimsFromUserInfoEndpoint = record.GetClaimsFromUserInfoEndpoint;
                    options.ClientId     = record.ClientId;
                    options.ClientSecret = record.ClientSecret;
                    options.SaveTokens   = true;
                    options.TokenValidationParameters.ValidateAudience = false;
                    options.Events.OnMessageReceived = async context =>
                    {
                    };
                    options.Events.OnTokenValidated = async context =>
                    {
                        var pipeLineStore = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipelineStore>();

                        OpenIdConnectMessage oidcMessage = null;
                        if (context.Options.ResponseType == "id_token")
                        {
                            oidcMessage = context.ProtocolMessage;
                        }
                        else
                        {
                            oidcMessage = context.TokenEndpointResponse;
                        }

                        var userState = context.ProtocolMessage.Parameters["state"].Split('.')[0];

                        var header  = new JwtHeader();
                        var handler = new JwtSecurityTokenHandler();
                        var idToken = handler.ReadJwtToken(oidcMessage.IdToken);
                        var claims  = idToken.Claims.ToList();

                        var stored = await pipeLineStore.GetOriginalIdTokenRequestAsync(userState);

                        DownstreamAuthorizeResponse idTokenResponse = new DownstreamAuthorizeResponse
                        {
                            Request       = stored,
                            AccessToken   = oidcMessage.AccessToken,
                            ExpiresAt     = oidcMessage.ExpiresIn,
                            IdToken       = oidcMessage.IdToken,
                            RefreshToken  = oidcMessage.RefreshToken,
                            TokenType     = oidcMessage.TokenType,
                            LoginProvider = scheme
                        };
                        await pipeLineStore.StoreDownstreamIdTokenResponseAsync(userState, idTokenResponse);
                    };
                    options.Events.OnRedirectToIdentityProvider = async context =>
                    {
                        var oidcPipelineKey = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipeLineKey>();

                        string key            = oidcPipelineKey.GetOIDCPipeLineKey();
                        var pipeLineStore     = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipelineStore>();
                        var stored            = await pipeLineStore.GetOriginalIdTokenRequestAsync(key);
                        var clientSecretStore = context.HttpContext.RequestServices.GetRequiredService <IOIDCPipelineClientStore>();

                        if (stored != null)
                        {
                            var clientRecord = await clientSecretStore.FetchClientRecordAsync(scheme,
                                                                                              stored.ClientId);

                            context.ProtocolMessage.ClientId = stored.ClientId;
                            context.Options.ClientId         = stored.ClientId;
                            context.Options.ClientSecret     = clientRecord.Secret;
                            context.ProtocolMessage.State    = $"{key}.";
                        }

                        context.Options.Authority = context.Options.Authority;
                        if (record.AdditionalProtocolScopes != null && record.AdditionalProtocolScopes.Any())
                        {
                            string additionalScopes = "";
                            foreach (var item in record.AdditionalProtocolScopes)
                            {
                                additionalScopes += $" {item}";
                            }
                            context.ProtocolMessage.Scope += additionalScopes;
                        }
                        if (context.HttpContext.User.Identity.IsAuthenticated)
                        {
                            // assuming a relogin trigger, so we will make the user relogin on the IDP
                            context.ProtocolMessage.Prompt = "login";
                        }
                        var allowedParams = await clientSecretStore.FetchAllowedProtocolParamatersAsync(scheme);

                        foreach (var allowedParam in allowedParams)
                        {
                            var item = stored.Raw[allowedParam];
                            if (item != null)
                            {
                                if (string.Compare(allowedParam, "state", true) == 0)
                                {
                                    context.ProtocolMessage.SetParameter(allowedParam, $"{key}.{item}");
                                }
                                else
                                {
                                    context.ProtocolMessage.SetParameter(allowedParam, item);
                                }
                            }
                        }

                        /*
                         * if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                         * {
                         *  context.ProtocolMessage.AcrValues = "v1=google";
                         * }
                         */
                    };
                    options.Events.OnRemoteFailure = context =>
                    {
                        context.Response.Redirect("/");
                        context.HandleResponse();
                        return(Task.CompletedTask);
                    };
                });
            }


            return(new IdentityBuilder(typeof(TUser), services));
        }
        /*
         * public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
         * {
         *  returnUrl = returnUrl ?? Url.Content("~/");
         *  if (remoteError != null)
         *  {
         *      ErrorMessage = $"Error from external provider: {remoteError}";
         *      return RedirectToPage("./Login", new {ReturnUrl = returnUrl });
         *  }
         *  var info = await _signInManager.GetExternalLoginInfoAsync();
         *  if (info == null)
         *  {
         *      ErrorMessage = "Error loading external login information.";
         *      return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
         *  }
         *
         *  // Sign in the user with this external login provider if the user already has a login.
         *  var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true);
         *  if (result.Succeeded)
         *  {
         *      _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
         *      return LocalRedirect(returnUrl);
         *  }
         *  if (result.IsLockedOut)
         *  {
         *      return RedirectToPage("./Lockout");
         *  }
         *  else
         *  {
         *      // If the user does not have an account, then ask the user to create an account.
         *      ReturnUrl = returnUrl;
         *      LoginProvider = info.LoginProvider;
         *      if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
         *      {
         *          Input = new InputModel
         *          {
         *              Email = info.Principal.FindFirstValue(ClaimTypes.Email)
         *          };
         *      }
         *      return Page();
         *  }
         * }
         */

        public async Task <IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
        {
            string currentNameIdClaimValue = null;

            if (User.Identity.IsAuthenticated)
            {
                // we will only create a new user if the user here is actually new.
                var qName = from claim in User.Claims
                            where claim.Type == ".nameIdentifier"
                            select claim;
                var nc = qName.FirstOrDefault();
                currentNameIdClaimValue = nc?.Value;
            }

            returnUrl = returnUrl ?? Url.Content("~/");
            if (remoteError != null)
            {
                ErrorMessage = $"Error from external provider: {remoteError}";
                return(RedirectToPage("./Login", new { ReturnUrl = returnUrl }));
            }
            var info = await _signInManager.GetExternalLoginInfoAsync();

            if (info == null)
            {
                ErrorMessage = "Error loading external login information.";
                return(RedirectToPage("./Login", new { ReturnUrl = returnUrl }));
            }
            var oidc = await HarvestOidcDataAsync();

            DownstreamAuthorizeResponse idTokenResponse = new DownstreamAuthorizeResponse
            {
                AccessToken   = oidc["access_token"],
                ExpiresAt     = oidc["expires_at"],
                IdToken       = oidc["id_token"],
                RefreshToken  = oidc["refresh_token"],
                TokenType     = oidc["token_type"],
                LoginProvider = info.LoginProvider
            };
            //       await _oidcPipelineStore.StoreDownstreamIdTokenResponseAsync(idTokenResponse);


            var queryNameId = from claim in info.Principal.Claims
                              where claim.Type == ClaimTypes.NameIdentifier
                              select claim;
            var nameIdClaim = queryNameId.FirstOrDefault();

            var query = from claim in info.Principal.Claims
                        where _possibleNameTypes.Contains(claim.Type)
                        select claim;
            var nameClaim   = query.FirstOrDefault();
            var displayName = nameIdClaim.Value;

            if (nameClaim != null)
            {
                displayName = nameClaim.Value;
            }
            if (currentNameIdClaimValue == nameIdClaim.Value)
            {
                // this is a re login from the same user, so don't do anything;
                return(LocalRedirect(returnUrl));
            }

            /*
             * // Sign in the user with this external login provider if the user already has a login.
             * var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true);
             *
             * if (result.Succeeded)
             * {
             *  // Update the token
             *  await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
             *
             *  _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
             *  return LocalRedirect(returnUrl);
             * }
             * if (result.IsLockedOut)
             * {
             *  return RedirectToPage("./Lockout");
             * }
             * else
             * {
             *  // If the user does not have an account, then ask the user to create an account.
             *  ReturnUrl = returnUrl;
             *  LoginProvider = info.LoginProvider;
             *  if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
             *  {
             *      Input = new InputModel
             *      {
             *          Email = info.Principal.FindFirstValue(ClaimTypes.Email)
             *      };
             *  }
             *  return Page();
             * }
             */
            var leftoverUser = await _userManager.FindByEmailAsync(displayName);

            if (leftoverUser != null)
            {
                await _userManager.DeleteAsync(leftoverUser); // just using this inMemory userstore as a scratch holding pad
            }
            var user = new ApplicationUser {
                UserName = nameIdClaim.Value, Email = displayName
            };

            var result = await _userManager.CreateAsync(user);

            if (result.Succeeded)
            {
                var newUser = await _userManager.FindByIdAsync(user.Id);

                var eClaims = new List <Claim>
                {
                    new Claim("display-name", displayName),
                    new Claim("login_provider", info.LoginProvider)
                };
                // normalized id.
                await _userManager.AddClaimsAsync(newUser, eClaims);

                await _signInManager.SignInAsync(user, isPersistent : false);

                await _userManager.DeleteAsync(user); // just using this inMemory userstore as a scratch holding pad

                _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);

                return(LocalRedirect(returnUrl));
            }
            return(RedirectToPage("./Login", new { ReturnUrl = returnUrl }));
        }
Ejemplo n.º 6
0
        public async Task <IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            if (remoteError != null)
            {
                ErrorMessage = $"Error from external provider: {remoteError}";
                return(RedirectToPage("./Login", new { ReturnUrl = returnUrl }));
            }
            var info = await _signInManager.GetExternalLoginInfoAsync();

            if (info == null)
            {
                ErrorMessage = "Error loading external login information.";
                return(RedirectToPage("./Login", new { ReturnUrl = returnUrl }));
            }
            var oidc = await HarvestOidcDataAsync();

            DownstreamAuthorizeResponse idTokenResponse = new DownstreamAuthorizeResponse
            {
                AccessToken   = oidc["access_token"],
                ExpiresAt     = oidc["expires_at"],
                IdToken       = oidc["id_token"],
                RefreshToken  = oidc["refresh_token"],
                TokenType     = oidc["token_type"],
                LoginProvider = info.LoginProvider
            };
            var externalPrincipalClaims = info.Principal.Claims.ToList();

            var nameIdentifier = GetValueFromClaim(externalPrincipalClaims, ClaimTypes.NameIdentifier);

            await _signInManager.SignOutAsync();

            var user = new IdentityUser
            {
                UserName = nameIdentifier,
                Email    = null
            };
            var result = await _userManager.CreateAsync(user);

            if (result.Succeeded)
            {
                var newUser = await _userManager.FindByIdAsync(user.Id);

                var eClaims = new List <Claim>
                {
                    new Claim(".displayName", user.Id),
                    new Claim(".externalNamedIdentitier", nameIdentifier),
                    new Claim(".loginProvider", info.LoginProvider)
                };

                // normalized id.
                await _userManager.AddClaimsAsync(newUser, eClaims);

                await _signInManager.SignInAsync(user, isPersistent : false);

                await _userManager.DeleteAsync(user); // just using this inMemory userstore as a scratch holding pad


                return(LocalRedirect(returnUrl));
            }
            return(RedirectToPage("./Login", new { ReturnUrl = returnUrl }));
        }
        private TokenRequestValidationResult ValidateAuthorizationCodeWithProofKeyParameters(string codeVerifier, DownstreamAuthorizeResponse idTokenResopnse)
        {
            if (idTokenResopnse.Request.CodeChallenge.IsMissing() || idTokenResopnse.Request.CodeChallengeMethod.IsMissing())
            {
                LogError("Client is missing code challenge or code challenge method", new { clientId = _validatedRequest.ClientId });
                return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
            }

            if (codeVerifier.IsMissing())
            {
                LogError("Missing code_verifier");
                return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
            }

            if (codeVerifier.Length < _options.InputLengthRestrictions.CodeVerifierMinLength ||
                codeVerifier.Length > _options.InputLengthRestrictions.CodeVerifierMaxLength)
            {
                LogError("code_verifier is too short or too long");
                return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
            }

            if (Constants.SupportedCodeChallengeMethods.Contains(idTokenResopnse.Request.CodeChallengeMethod) == false)
            {
                LogError("Unsupported code challenge method", new { codeChallengeMethod = idTokenResopnse.Request.CodeChallengeMethod });
                return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
            }

            if (ValidateCodeVerifierAgainstCodeChallenge(codeVerifier, idTokenResopnse.Request.CodeChallenge, idTokenResopnse.Request.CodeChallengeMethod) == false)
            {
                LogError("Transformed code verifier does not match code challenge");
                return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
            }

            return(Valid());
        }