Example #1
0
        private async Task <SecurityToken> CreateSecurityTokenAsync(SignInValidationResult result, ClaimsIdentity outgoingSubject)
        {
            var credential = await _keys.GetSigningCredentialsAsync();

            var key = credential.Key as Microsoft.IdentityModel.Tokens.X509SecurityKey;

            var descriptor = new SecurityTokenDescriptor
            {
                Audience           = result.Client.ClientId,
                IssuedAt           = DateTime.UtcNow,
                NotBefore          = DateTime.UtcNow,
                Expires            = DateTime.UtcNow.AddSeconds(result.Client.IdentityTokenLifetime),
                SigningCredentials = new SigningCredentials(key, result.RelyingParty.SignatureAlgorithm, result.RelyingParty.DigestAlgorithm),
                Subject            = outgoingSubject,
                Issuer             = _contextAccessor.HttpContext.GetIdentityServerIssuerUri(),
            };

            if (result.RelyingParty.EncryptionCertificate != null)
            {
                descriptor.EncryptingCredentials = new X509EncryptingCredentials(result.RelyingParty.EncryptionCertificate);
            }

            var handler = CreateTokenHandler(result.RelyingParty.TokenType);

            return(CreateToken(handler, descriptor));
        }
Example #2
0
        /// <summary>
        /// Creates the JWT header
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns>The JWT header</returns>
        protected virtual async Task <JwtHeader> CreateHeaderAsync(Token token)
        {
            var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms);

            if (credential == null)
            {
                throw new InvalidOperationException("No signing credential is configured. Can't create JWT token");
            }

            var header = new JwtHeader(credential);

            // emit x5t claim for backwards compatibility with v4 of MS JWT library
            if (credential.Key is X509SecurityKey x509Key)
            {
                var cert = x509Key.Certificate;
                if (Clock.UtcNow.UtcDateTime > cert.NotAfter)
                {
                    Logger.LogWarning("Certificate {subjectName} has expired on {expiration}", cert.Subject, cert.NotAfter.ToString(CultureInfo.InvariantCulture));
                }

                header["x5t"] = Base64Url.Encode(cert.GetCertHash());
            }

            if (token.Type == TokenTypes.AccessToken)
            {
                if (Options.AccessTokenJwtType.IsPresent())
                {
                    header["typ"] = Options.AccessTokenJwtType;
                }
            }

            return(header);
        }
        /// <summary>
        /// Creates the JWT header
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns>The JWT header</returns>
        protected virtual async Task <JwtHeader> CreateHeaderAsync(Token token)
        {
            var credential = await Keys.GetSigningCredentialsAsync();

            if (credential == null)
            {
                throw new InvalidOperationException("No signing credential is configured. Can't create JWT token");
            }

            var header = new JwtHeader(credential);

            // emit x5t claim for backwards compatibility with v4 of MS JWT library
            if (credential.Key is X509SecurityKey x509key)
            {
                var cert = x509key.Certificate;
                if (IdentityServerDateTime.UtcNow > cert.NotAfter)
                {
                    Logger.LogWarning("Certificate {subjectName} has expired on {expiration}", cert.Subject, cert.NotAfter.ToString());
                }

                header.Add("x5t", Base64Url.Encode(cert.GetCertHash()));
            }

            return(header);
        }
Example #4
0
        public async Task <string> GenerateSerializedRstr(ValidatedWsFederationSigninRequest request)
        {
            var now = _clock.UtcNow.UtcDateTime;

            var principal   = request.Subject.Identity as ClaimsIdentity;
            var nameIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier);

            if (nameIdClaim == null)
            {
                nameIdClaim = new Claim(ClaimTypes.NameIdentifier, principal.Name);
                nameIdClaim.Properties.Add(ClaimProperties.SamlNameIdentifierFormat, Saml2Constants.NameIdentifierFormats.UnspecifiedString);
                principal.AddClaim(nameIdClaim);
            }

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Audience           = request.RequestMessage.Wtrealm,
                Expires            = now.AddSeconds(request.Client.IdentityTokenLifetime),
                IssuedAt           = now,
                Issuer             = _options.IssuerUri,
                NotBefore          = now,
                SigningCredentials = await _keys.GetSigningCredentialsAsync(),
                Subject            = principal
            };

            //For whatever reason, the Digest method isn't specified in the builder extensions for identity server.
            //Not a good solution to force the user to use th eoverload that takes SigningCredentials
            //IdentityServer4/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs
            //Instead, it should be supported in:
            //  The overload that takes a X509Certificate2
            //  The overload that looks it up in a cert store
            //  The overload that takes an RsaSecurityKey
            //  AddDeveloperSigningCredential
            //For now, this is a workaround.
            if (tokenDescriptor.SigningCredentials.Digest == null)
            {
                _logger.LogInformation($"SigningCredentials does not have a digest specified. Using default digest algorithm of {SecurityAlgorithms.Sha256Digest}");
                tokenDescriptor.SigningCredentials = new SigningCredentials(tokenDescriptor.SigningCredentials.Key, tokenDescriptor.SigningCredentials.Algorithm, SecurityAlgorithms.Sha256Digest);
            }

            _logger.LogDebug("Creating SAML 2.0 security token.");
            var tokenHandler = new Saml2SecurityTokenHandler();
            var token        = tokenHandler.CreateToken(tokenDescriptor);

            _logger.LogDebug("Serializing RSTR.");
            var rstr = new RequestSecurityTokenResponse
            {
                AppliesTo = new AppliesTo(request.RequestMessage.Wtrealm),
                KeyType   = "http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey",
                Lifetime  = new Lifetime(now, now.AddSeconds(request.Client.IdentityTokenLifetime)),
                RequestedSecurityToken = token,
                RequestType            = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue",
                TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
            };

            return(RequestSecurityTokenResponseSerializer.Serialize(rstr));
        }
        /// <summary>
        /// Creates the JWK document.
        /// </summary>
        public virtual async Task <IEnumerable <Models.JsonWebKey> > CreateJwkDocumentAsync()
        {
            var webKeys            = new List <Models.JsonWebKey>();
            var signingCredentials = await Keys.GetSigningCredentialsAsync();

            var algorithm = signingCredentials?.Algorithm ?? Constants.SigningAlgorithms.RSA_SHA_256;

            foreach (var key in await Keys.GetValidationKeysAsync())
            {
                if (key is X509SecurityKey x509Key)
                {
                    var cert64     = Convert.ToBase64String(x509Key.Certificate.RawData);
                    var thumbprint = Base64Url.Encode(x509Key.Certificate.GetCertHash());

                    var pubKey     = x509Key.PublicKey as RSA;
                    var parameters = pubKey.ExportParameters(false);
                    var exponent   = Base64Url.Encode(parameters.Exponent);
                    var modulus    = Base64Url.Encode(parameters.Modulus);

                    var webKey = new Models.JsonWebKey
                    {
                        kty = "RSA",
                        use = "sig",
                        kid = x509Key.KeyId,
                        x5t = thumbprint,
                        e   = exponent,
                        n   = modulus,
                        x5c = new[] { cert64 },
                        alg = algorithm
                    };

                    webKeys.Add(webKey);
                    continue;
                }

                if (key is RsaSecurityKey rsaKey)
                {
                    var parameters = rsaKey.Rsa?.ExportParameters(false) ?? rsaKey.Parameters;
                    var exponent   = Base64Url.Encode(parameters.Exponent);
                    var modulus    = Base64Url.Encode(parameters.Modulus);

                    var webKey = new Models.JsonWebKey
                    {
                        kty = "RSA",
                        use = "sig",
                        kid = rsaKey.KeyId,
                        e   = exponent,
                        n   = modulus,
                        alg = algorithm
                    };

                    webKeys.Add(webKey);
                }
            }

            return(webKeys);
        }
Example #6
0
        public async Task <string> GenerateSerializedRstr(ValidatedWsFederationRequest request)
        {
            var now        = _clock.UtcNow.UtcDateTime;
            var credential = await _keys.GetSigningCredentialsAsync();

            var key = credential.Key as X509SecurityKey;

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Audience           = request.RequestMessage.Wtrealm,
                Expires            = now.AddSeconds(request.Client.IdentityTokenLifetime),
                IssuedAt           = now,
                Issuer             = _options.IssuerUri,
                NotBefore          = now,
                SigningCredentials = key == null ? credential : new X509SigningCredentials(key.Certificate, _federationOptions.DefaultSignatureAlgorithm),
                Subject            = await CreateSubjectAsync(request)
            };

            //For whatever reason, the Digest method isn't specified in the builder extensions for identity server.
            //Not a good solution to force the user to use the overload that takes SigningCredentials
            //IdentityServer4/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs
            //Instead, it should be supported in:
            //  The overload that takes a X509Certificate2
            //  The overload that looks it up in a cert store
            //  The overload that takes an RsaSecurityKey
            //  AddDeveloperSigningCredential
            //For now, this is a workaround.
            if (tokenDescriptor.SigningCredentials.Digest == null)
            {
                _logger.LogInformation($"SigningCredentials does not have a digest specified. Using default digest algorithm of {SecurityAlgorithms.Sha256Digest}");
                tokenDescriptor.SigningCredentials = new SigningCredentials(tokenDescriptor.SigningCredentials.Key, tokenDescriptor.SigningCredentials.Algorithm ?? _federationOptions.DefaultSignatureAlgorithm, _federationOptions.DefaultDigestAlgorithm);
            }

            _logger.LogDebug("Creating SAML 2.0 security token.");

            var tokenHandler = new Saml2SecurityTokenHandler();
            var token        = tokenHandler.CreateToken(tokenDescriptor);

            _logger.LogDebug("Serializing RSTR.");
            var rstr = new RequestSecurityTokenResponse
            {
                AppliesTo = new AppliesTo(request.RequestMessage.Wtrealm),
                KeyType   = "http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey",
                Lifetime  = new Lifetime
                {
                    Created = XmlConvert.ToString(now, XmlDateTimeSerializationMode.Utc),
                    Expires = XmlConvert.ToString(now.AddSeconds(request.Client.IdentityTokenLifetime), XmlDateTimeSerializationMode.Utc),
                },
                RequestedSecurityToken = token,
                RequestType            = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue",
                TokenType = WsFederationConstants.TokenTypes.Saml2TokenProfile11
            };

            return(RequestSecurityTokenResponseSerializer.Serialize(rstr));
        }
        public virtual async Task <Token> CreateIdentityTokenAsync(TokenCreationRequest request)
        {
            Logger.LogTrace("Creating identity token");
            request.Validate();
            string       algorithm = ((await KeyMaterialService.GetSigningCredentialsAsync(request.ValidatedRequest.Client.AllowedIdentityTokenSigningAlgorithms)) ?? throw new InvalidOperationException("No signing credential is configured.")).Algorithm;
            List <Claim> claims    = new List <Claim>();

            if (request.Nonce.IsPresent())
            {
                claims.Add(new Claim("nonce", request.Nonce));
            }
            claims.Add(new Claim("iat", Clock.UtcNow.ToUnixTimeSeconds().ToString(), "http://www.w3.org/2001/XMLSchema#integer64"));
            if (request.AccessTokenToHash.IsPresent())
            {
                claims.Add(new Claim("at_hash", CryptoHelper.CreateHashClaimValue(request.AccessTokenToHash, algorithm)));
            }
            if (request.AuthorizationCodeToHash.IsPresent())
            {
                claims.Add(new Claim("c_hash", CryptoHelper.CreateHashClaimValue(request.AuthorizationCodeToHash, algorithm)));
            }
            if (request.StateHash.IsPresent())
            {
                claims.Add(new Claim("s_hash", request.StateHash));
            }
            if (request.ValidatedRequest.SessionId.IsPresent())
            {
                claims.Add(new Claim("sid", request.ValidatedRequest.SessionId));
            }
            List <Claim> list = claims;

            list.AddRange(await ClaimsProvider.GetIdentityTokenClaimsAsync(request.Subject, request.ValidatedResources, request.IncludeAllIdentityClaims, request.ValidatedRequest));
            string identityServerIssuerUri = ContextAccessor.HttpContext.GetIdentityServerIssuerUri();

            return(new Token("id_token")
            {
                CreationTime = Clock.UtcNow.UtcDateTime,
                Audiences =
                {
                    request.ValidatedRequest.Client.ClientId
                },
                Issuer = identityServerIssuerUri,
                Lifetime = request.ValidatedRequest.Client.IdentityTokenLifetime,
                Claims = claims.Distinct(new ClaimComparer()).ToList(),
                ClientId = request.ValidatedRequest.Client.ClientId,
                AccessTokenType = request.ValidatedRequest.AccessTokenType,
                AllowedSigningAlgorithms = request.ValidatedRequest.Client.AllowedIdentityTokenSigningAlgorithms
            });
        }
        /// <summary>
        /// Creates JWT token
        /// </summary>
        /// <param name="token"></param>
        /// <param name="payload"></param>
        /// <param name="headerElements"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        protected virtual async Task <string> CreateJwtAsync(Token token, string payload,
                                                             Dictionary <string, object> headerElements)
        {
            var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms);

            if (credential == null)
            {
                throw new InvalidOperationException("No signing credential is configured. Can't create JWT token");
            }

            var handler = new JsonWebTokenHandler {
                SetDefaultTimesOnTokenCreation = false
            };

            return(handler.CreateToken(payload, credential, headerElements));
        }
        public async Task <EntityDescriptor> GenerateAsync(string wsfedEndpoint)
        {
            var signingKey = (await keys.GetSigningCredentialsAsync()).Key as X509SecurityKey;
            var cert       = signingKey.Certificate;

            var applicationDescriptor  = GetApplicationDescriptor(wsfedEndpoint, cert);
            var tokenServiceDescriptor = GetTokenServiceDescriptor(wsfedEndpoint, cert);

            var id     = new EntityId(contextAccessor.HttpContext.GetIdentityServerIssuerUri());
            var entity = new EntityDescriptor(id);

            entity.SigningCredentials = new X509SigningCredentials(cert);
            entity.RoleDescriptors.Add(applicationDescriptor);
            entity.RoleDescriptors.Add(tokenServiceDescriptor);

            return(entity);
        }
Example #10
0
        public async Task <WsFederationConfiguration> GenerateAsync(string wsfedEndpoint)
        {
            var signingKey         = (await _keys.GetSigningCredentialsAsync()).Key as X509SecurityKey;
            var cert               = signingKey.Certificate;
            var issuer             = _contextAccessor.HttpContext.GetIdentityServerIssuerUri();
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
            var config             = new WsFederationConfiguration()
            {
                Issuer             = issuer,
                TokenEndpoint      = wsfedEndpoint,
                SigningCredentials = signingCredentials,
            };

            config.SigningKeys.Add(signingKey);
            config.KeyInfos.Add(new KeyInfo(cert));

            return(config);
        }
Example #11
0
        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments(_options.MetadataEndpoint, StringComparison.InvariantCultureIgnoreCase))
            {
                _logger.LogInformation("Received metadata request");

                var samlSegment  = _options.Saml20Endpoint;
                var wsFedSegment = _options.WsFedEndpoint;

                var samlUrl  = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.PathBase}{samlSegment}/";
                var wsFedUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.PathBase}{wsFedSegment}/";

                _logger.LogInformation($"Using Saml Url {samlUrl} in Metadata request");
                _logger.LogInformation($"Using WsFed Url {wsFedUrl} in Metadata request");

                var key = ((await _keyService.GetSigningCredentialsAsync()).Key as X509SecurityKey).Certificate;

                _logger.LogInformation($"Using Certificate Thumbprint {key.Thumbprint} in Metadata request");

                var sb = new StringBuilder();
                using (var xmlWriter = XmlWriter.Create(new StringWriter(sb)))
                {
                    _serializer.Serialize(xmlWriter,
                                          key,
                                          DocumentId,
                                          _options.IssuerName,
                                          samlUrl,
                                          wsFedUrl);
                }

                var xml       = sb.ToString();
                var signedXml = SignXml(xml, key);

                context.Response.ContentType = "application/xml";
                await context.Response.WriteAsync(signedXml);

                _logger.LogInformation("Metadata generated successfully");

                return;
            }

            await _next(context);
        }
Example #12
0
        public async Task <string> GetMetadata(HttpContext context)
        {
            var configuration = new WsFederationConfiguration
            {
                Issuer             = _options.IssuerUri,
                SigningCredentials = await _keys.GetSigningCredentialsAsync(),
                TokenEndpoint      = context.GetIdentityServerOrigin() + "/wsfederation"
            };

            //For whatever reason, the Digest method isn't specified in the builder extensions for identity server.
            //Not a good solution to force the user to use the overload that takes SigningCredentials
            //IdentityServer4/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs
            //Instead, it should be supported in:
            //  The overload that takes a X509Certificate2
            //  The overload that looks it up in a cert store
            //  The overload that takes an RsaSecurityKey
            //  AddDeveloperSigningCredential
            //For now, this is a workaround.
            if (configuration.SigningCredentials.Digest == null)
            {
                _logger.LogInformation($"SigningCredentials does not have a digest specified. Using default digest algorithm of {SecurityAlgorithms.Sha256Digest}");
                configuration.SigningCredentials = new SigningCredentials(configuration.SigningCredentials.Key, configuration.SigningCredentials.Algorithm ?? SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
            }
            configuration.KeyInfos.Add(new KeyInfo(configuration.SigningCredentials.Key));

            var serializer = new WsFederationMetadataSerializer();

            var sb       = new StringBuilder();
            var settings = new XmlWriterSettings
            {
                OmitXmlDeclaration = true
            };

            using (var writer = XmlWriter.Create(sb, settings))
            {
                serializer.WriteMetadata(writer, configuration);
            }
            var metadata = sb.ToString();

            return(metadata);
        }
Example #13
0
        /// <summary>
        /// Creates the JWT header
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="credential">The credentials.</param>
        /// <returns>The JWT header</returns>
        protected virtual async Task <JwtHeader> CreateHeaderAsync(Token token)
        {
            var credential = await _keys.GetSigningCredentialsAsync();

            if (credential == null)
            {
                throw new InvalidOperationException("No signing credential is configured. Can't create JWT token");
            }

            var header = new JwtHeader(credential);

            // emit x5t claim for backwards compatibility with v4 of MS JWT library
            var x509key = credential.Key as X509SecurityKey;

            if (x509key != null)
            {
                header.Add("x5t", Base64Url.Encode(x509key.Certificate.GetCertHash()));
            }

            return(header);
        }
        protected virtual async Task <JwtHeader> CreateHeaderAsync(Token token)
        {
            SigningCredentials obj             = (await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms)) ?? throw new InvalidOperationException("No signing credential is configured. Can't create JWT token");
            JwtHeader          jwtHeader       = new JwtHeader(obj);
            X509SecurityKey    x509SecurityKey = obj.Key as X509SecurityKey;

            if (x509SecurityKey != null)
            {
                X509Certificate2 certificate = x509SecurityKey.Certificate;
                if (Clock.UtcNow.UtcDateTime > certificate.NotAfter)
                {
                    Logger.LogWarning("Certificate {subjectName} has expired on {expiration}", certificate.Subject, certificate.NotAfter.ToString(CultureInfo.InvariantCulture));
                }
                jwtHeader["x5t"] = Base64Url.Encode(certificate.GetCertHash());
            }
            if (token.Type == "access_token" && !string.IsNullOrWhiteSpace(Options.AccessTokenJwtType))
            {
                jwtHeader["typ"] = Options.AccessTokenJwtType;
            }
            return(jwtHeader);
        }
        private async Task <SecurityToken> CreateSecurityTokenAsync(SignInValidationResult result, ClaimsIdentity outgoingSubject)
        {
            var credential = await _keys.GetSigningCredentialsAsync();

            var key = credential.Key as Microsoft.IdentityModel.Tokens.X509SecurityKey;

            var descriptor = new SecurityTokenDescriptor
            {
                AppliesToAddress   = result.Client.ClientId,
                Lifetime           = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddSeconds(result.Client.IdentityTokenLifetime)),
                ReplyToAddress     = result.Client.RedirectUris.First(),
                SigningCredentials = new X509SigningCredentials(key.Certificate, result.RelyingParty.SignatureAlgorithm, result.RelyingParty.DigestAlgorithm),
                Subject            = outgoingSubject,
                TokenIssuerName    = _contextAccessor.HttpContext.GetIdentityServerIssuerUri(),
                TokenType          = result.RelyingParty.TokenType
            };

            if (result.RelyingParty.EncryptionCertificate != null)
            {
                descriptor.EncryptingCredentials = new EncryptedKeyEncryptingCredentials(result.RelyingParty.EncryptionCertificate);
            }

            return(CreateSupportedSecurityTokenHandler().CreateToken(descriptor));
        }
        /// <summary>
        /// Creates the response for a implicit flow request
        /// </summary>
        /// <param name="request"></param>
        /// <param name="authorizationCode"></param>
        /// <returns></returns>
        protected virtual async Task <AuthorizeResponse> CreateImplicitFlowResponseAsync(ValidatedAuthorizeRequest request, string authorizationCode = null)
        {
            Logger.LogDebug("Creating Implicit Flow response.");

            string accessTokenValue    = null;
            int    accessTokenLifetime = 0;

            var responseTypes = request.ResponseType.FromSpaceSeparatedString();

            if (responseTypes.Contains(OidcConstants.ResponseTypes.Token))
            {
                var tokenRequest = new TokenCreationRequest
                {
                    Subject   = request.Subject,
                    Resources = request.ValidatedScopes.GrantedResources,

                    ValidatedRequest = request
                };

                var accessToken = await TokenService.CreateAccessTokenAsync(tokenRequest);

                accessTokenLifetime = accessToken.Lifetime;

                accessTokenValue = await TokenService.CreateSecurityTokenAsync(accessToken);
            }

            string jwt = null;

            if (responseTypes.Contains(OidcConstants.ResponseTypes.IdToken))
            {
                string stateHash = null;
                if (request.State.IsPresent())
                {
                    var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.Client.AllowedIdentityTokenSigningAlgorithms);

                    if (credential == null)
                    {
                        throw new InvalidOperationException("No signing credential is configured.");
                    }

                    var algorithm = credential.Algorithm;
                    stateHash = CryptoHelper.CreateHashClaimValue(request.State, algorithm);
                }

                var tokenRequest = new TokenCreationRequest
                {
                    ValidatedRequest         = request,
                    Subject                  = request.Subject,
                    Resources                = request.ValidatedScopes.GrantedResources,
                    Nonce                    = request.Raw.Get(OidcConstants.AuthorizeRequest.Nonce),
                    IncludeAllIdentityClaims = !request.AccessTokenRequested,
                    AccessTokenToHash        = accessTokenValue,
                    AuthorizationCodeToHash  = authorizationCode,
                    StateHash                = stateHash
                };

                var idToken = await TokenService.CreateIdentityTokenAsync(tokenRequest);

                jwt = await TokenService.CreateSecurityTokenAsync(idToken);
            }

            var response = new AuthorizeResponse
            {
                Request             = request,
                AccessToken         = accessTokenValue,
                AccessTokenLifetime = accessTokenLifetime,
                IdentityToken       = jwt,
                SessionState        = request.GenerateSessionStateValue()
            };

            return(response);
        }
        /// <summary>
        /// Creates an identity token.
        /// </summary>
        /// <param name="request">The token creation request.</param>
        /// <returns>
        /// An identity token
        /// </returns>
        public virtual async Task <Token> CreateIdentityTokenAsync(TokenCreationRequest request)
        {
            Logger.LogTrace("Creating identity token");
            request.Validate();

            // todo: Dom, add a test for this. validate the at and c hashes are correct for the id_token when the client's alg doesn't match the server default.
            var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.ValidatedRequest.Client.AllowedIdentityTokenSigningAlgorithms);

            if (credential == null)
            {
                throw new InvalidOperationException("No signing credential is configured.");
            }

            var signingAlgorithm = credential.Algorithm;

            // host provided claims
            var claims = new List <Claim>();

            // if nonce was sent, must be mirrored in id token
            if (request.Nonce.IsPresent())
            {
                claims.Add(new Claim(JwtClaimTypes.Nonce, request.Nonce));
            }

            // add iat claim
            claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64));

            // add at_hash claim
            if (request.AccessTokenToHash.IsPresent())
            {
                claims.Add(new Claim(JwtClaimTypes.AccessTokenHash, CryptoHelper.CreateHashClaimValue(request.AccessTokenToHash, signingAlgorithm)));
            }

            // add c_hash claim
            if (request.AuthorizationCodeToHash.IsPresent())
            {
                claims.Add(new Claim(JwtClaimTypes.AuthorizationCodeHash, CryptoHelper.CreateHashClaimValue(request.AuthorizationCodeToHash, signingAlgorithm)));
            }

            // add s_hash claim
            if (request.StateHash.IsPresent())
            {
                claims.Add(new Claim(JwtClaimTypes.StateHash, request.StateHash));
            }

            // add sid if present
            if (request.ValidatedRequest.SessionId.IsPresent())
            {
                claims.Add(new Claim(JwtClaimTypes.SessionId, request.ValidatedRequest.SessionId));
            }

            claims.AddRange(await ClaimsProvider.GetIdentityTokenClaimsAsync(
                                request.Subject,
                                request.ValidatedResources,
                                request.IncludeAllIdentityClaims,
                                request.ValidatedRequest));

            var issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri();

            var token = new Token(OidcConstants.TokenTypes.IdentityToken)
            {
                CreationTime             = Clock.UtcNow.UtcDateTime,
                Audiences                = { request.ValidatedRequest.Client.ClientId },
                Issuer                   = issuer,
                Lifetime                 = request.ValidatedRequest.Client.IdentityTokenLifetime,
                Claims                   = claims.Distinct(new ClaimComparer()).ToList(),
                ClientId                 = request.ValidatedRequest.Client.ClientId,
                AccessTokenType          = request.ValidatedRequest.AccessTokenType,
                AllowedSigningAlgorithms = request.ValidatedRequest.Client.AllowedIdentityTokenSigningAlgorithms
            };

            return(token);
        }
Example #18
0
        /// <summary>
        /// Creates the discovery document.
        /// </summary>
        /// <param name="baseUrl">The base URL.</param>
        /// <param name="issuerUri">The issuer URI.</param>
        public virtual async Task <Dictionary <string, object> > CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri)
        {
            var entries = new Dictionary <string, object>
            {
                { OidcConstants.Discovery.Issuer, issuerUri }
            };

            // jwks
            if (Options.Discovery.ShowKeySet)
            {
                if ((await Keys.GetValidationKeysAsync()).Any())
                {
                    entries.Add(OidcConstants.Discovery.JwksUri, baseUrl + Constants.ProtocolRoutePaths.DiscoveryWebKeys);
                }
            }

            // endpoints
            if (Options.Discovery.ShowEndpoints)
            {
                if (Options.Endpoints.EnableAuthorizeEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.AuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Authorize);
                }

                if (Options.Endpoints.EnableTokenEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.TokenEndpoint, baseUrl + Constants.ProtocolRoutePaths.Token);
                }

                if (Options.Endpoints.EnableUserInfoEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.UserInfoEndpoint, baseUrl + Constants.ProtocolRoutePaths.UserInfo);
                }

                if (Options.Endpoints.EnableEndSessionEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.EndSessionEndpoint, baseUrl + Constants.ProtocolRoutePaths.EndSession);
                }

                if (Options.Endpoints.EnableCheckSessionEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.CheckSessionIframe, baseUrl + Constants.ProtocolRoutePaths.CheckSession);
                }

                if (Options.Endpoints.EnableTokenRevocationEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.RevocationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Revocation);
                }

                if (Options.Endpoints.EnableIntrospectionEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.IntrospectionEndpoint, baseUrl + Constants.ProtocolRoutePaths.Introspection);
                }

                if (Options.Endpoints.EnableDeviceAuthorizationEndpoint)
                {
                    entries.Add(OidcConstants.Discovery.DeviceAuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.DeviceAuthorization);
                }

                if (Options.MutualTls.Enabled)
                {
                    var mtlsEndpoints = new Dictionary <string, string>();

                    if (Options.Endpoints.EnableTokenEndpoint)
                    {
                        mtlsEndpoints.Add(OidcConstants.Discovery.TokenEndpoint, baseUrl + Constants.ProtocolRoutePaths.MtlsToken);
                    }
                    if (Options.Endpoints.EnableTokenRevocationEndpoint)
                    {
                        mtlsEndpoints.Add(OidcConstants.Discovery.RevocationEndpoint, baseUrl + Constants.ProtocolRoutePaths.MtlsRevocation);
                    }
                    if (Options.Endpoints.EnableIntrospectionEndpoint)
                    {
                        mtlsEndpoints.Add(OidcConstants.Discovery.IntrospectionEndpoint, baseUrl + Constants.ProtocolRoutePaths.MtlsIntrospection);
                    }
                    if (Options.Endpoints.EnableDeviceAuthorizationEndpoint)
                    {
                        mtlsEndpoints.Add(OidcConstants.Discovery.DeviceAuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.MtlsDeviceAuthorization);
                    }

                    if (mtlsEndpoints.Any())
                    {
                        entries.Add(OidcConstants.Discovery.MtlsEndpointAliases, mtlsEndpoints);
                    }
                }
            }

            // logout
            if (Options.Endpoints.EnableEndSessionEndpoint)
            {
                entries.Add(OidcConstants.Discovery.FrontChannelLogoutSupported, true);
                entries.Add(OidcConstants.Discovery.FrontChannelLogoutSessionSupported, true);
                entries.Add(OidcConstants.Discovery.BackChannelLogoutSupported, true);
                entries.Add(OidcConstants.Discovery.BackChannelLogoutSessionSupported, true);
            }

            // scopes and claims
            if (Options.Discovery.ShowIdentityScopes ||
                Options.Discovery.ShowApiScopes ||
                Options.Discovery.ShowClaims)
            {
                var resources = await ResourceStore.GetAllEnabledResourcesAsync();

                var scopes = new List <string>();

                // scopes
                if (Options.Discovery.ShowIdentityScopes)
                {
                    scopes.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).Select(x => x.Name));
                }

                if (Options.Discovery.ShowApiScopes)
                {
                    var apiScopes = from api in resources.ApiResources
                                    from scope in api.Scopes
                                    where scope.ShowInDiscoveryDocument
                                    select scope.Name;

                    scopes.AddRange(apiScopes);
                    scopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess);
                }

                if (scopes.Any())
                {
                    entries.Add(OidcConstants.Discovery.ScopesSupported, scopes.ToArray());
                }

                // claims
                if (Options.Discovery.ShowClaims)
                {
                    var claims = new List <string>();

                    // add non-hidden identity scopes related claims
                    claims.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).SelectMany(x => x.UserClaims));

                    // add non-hidden api scopes related claims
                    foreach (var resource in resources.ApiResources)
                    {
                        claims.AddRange(resource.UserClaims);

                        foreach (var scope in resource.Scopes)
                        {
                            if (scope.ShowInDiscoveryDocument)
                            {
                                claims.AddRange(scope.UserClaims);
                            }
                        }
                    }

                    entries.Add(OidcConstants.Discovery.ClaimsSupported, claims.Distinct().ToArray());
                }
            }

            // grant types
            if (Options.Discovery.ShowGrantTypes)
            {
                var standardGrantTypes = new List <string>
                {
                    OidcConstants.GrantTypes.AuthorizationCode,
                    OidcConstants.GrantTypes.ClientCredentials,
                    OidcConstants.GrantTypes.RefreshToken,
                    OidcConstants.GrantTypes.Implicit
                };

                if (!(ResourceOwnerValidator is NotSupportedResourceOwnerPasswordValidator))
                {
                    standardGrantTypes.Add(OidcConstants.GrantTypes.Password);
                }

                if (Options.Endpoints.EnableDeviceAuthorizationEndpoint)
                {
                    standardGrantTypes.Add(OidcConstants.GrantTypes.DeviceCode);
                }

                var showGrantTypes = new List <string>(standardGrantTypes);

                if (Options.Discovery.ShowExtensionGrantTypes)
                {
                    showGrantTypes.AddRange(ExtensionGrants.GetAvailableGrantTypes());
                }

                entries.Add(OidcConstants.Discovery.GrantTypesSupported, showGrantTypes.ToArray());
            }

            // response types
            if (Options.Discovery.ShowResponseTypes)
            {
                entries.Add(OidcConstants.Discovery.ResponseTypesSupported, Constants.SupportedResponseTypes.ToArray());
            }

            // response modes
            if (Options.Discovery.ShowResponseModes)
            {
                entries.Add(OidcConstants.Discovery.ResponseModesSupported, Constants.SupportedResponseModes.ToArray());
            }

            // misc
            if (Options.Discovery.ShowTokenEndpointAuthenticationMethods)
            {
                var types = SecretParsers.GetAvailableAuthenticationMethods().ToList();
                if (Options.MutualTls.Enabled)
                {
                    types.Add(OidcConstants.EndpointAuthenticationMethods.TlsClientAuth);
                    types.Add(OidcConstants.EndpointAuthenticationMethods.SelfSignedTlsClientAuth);
                }

                entries.Add(OidcConstants.Discovery.TokenEndpointAuthenticationMethodsSupported, types);
            }

            var signingCredentials = await Keys.GetSigningCredentialsAsync();

            if (signingCredentials != null)
            {
                var algorithm = signingCredentials.Algorithm;
                entries.Add(OidcConstants.Discovery.IdTokenSigningAlgorithmsSupported, new[] { algorithm });
            }

            entries.Add(OidcConstants.Discovery.SubjectTypesSupported, new[] { "public" });
            entries.Add(OidcConstants.Discovery.CodeChallengeMethodsSupported, new[] { OidcConstants.CodeChallengeMethods.Plain, OidcConstants.CodeChallengeMethods.Sha256 });

            if (Options.Endpoints.EnableAuthorizeEndpoint)
            {
                entries.Add(OidcConstants.Discovery.RequestParameterSupported, true);

                if (Options.Endpoints.EnableJwtRequestUri)
                {
                    entries.Add(OidcConstants.Discovery.RequestUriParameterSupported, true);
                }
            }

            if (Options.MutualTls.Enabled)
            {
                entries.Add(OidcConstants.Discovery.TlsClientCertificateBoundAccessTokens, true);
            }

            // custom entries
            if (!Options.Discovery.CustomEntries.IsNullOrEmpty())
            {
                foreach (var customEntry in Options.Discovery.CustomEntries)
                {
                    if (entries.ContainsKey(customEntry.Key))
                    {
                        Logger.LogError("Discovery custom entry {key} cannot be added, because it already exists.", customEntry.Key);
                    }
                    else
                    {
                        if (customEntry.Value is string customValueString)
                        {
                            if (customValueString.StartsWith("~/") && Options.Discovery.ExpandRelativePathsInCustomEntries)
                            {
                                entries.Add(customEntry.Key, baseUrl + customValueString.Substring(2));
                                continue;
                            }
                        }

                        entries.Add(customEntry.Key, customEntry.Value);
                    }
                }
            }

            return(entries);
        }
Example #19
0
        private async Task <Saml2SecurityToken> CreateSecurityTokenAsync(SignInRequest request, RelyingParty rp, ClaimsIdentity outgoingSubject)
        {
            var now = DateTime.Now;

            var outgoingNameId = outgoingSubject.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);

            if (outgoingNameId == null)
            {
                _logger.LogError("The user profile does not have a name id");

                throw new SignInException("The user profile does not have a name id");
            }

            var issuer = new Saml2NameIdentifier(_options.IssuerName);

            var nameId = new Saml2NameIdentifier(outgoingNameId.Value);

            var subjectConfirmationData = new Saml2SubjectConfirmationData();

            subjectConfirmationData.NotOnOrAfter = now.AddMinutes(
                rp.TokenLifetimeInMinutes.GetValueOrDefault(_options.DefaultNotOnOrAfterInMinutes));

            if (request.Parameters.ContainsKey("Recipient"))
            {
                subjectConfirmationData.Recipient = new Uri(request.Parameters["Recipient"]);
            }
            else
            {
                subjectConfirmationData.Recipient = new Uri(rp.ReplyUrl);
            }

            var subjectConfirmation = new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:bearer"),
                                                                   subjectConfirmationData);

            subjectConfirmation.NameIdentifier = nameId;

            var subject = new Saml2Subject(subjectConfirmation);

            var conditions = new Saml2Conditions(new Saml2AudienceRestriction[]
            {
                new Saml2AudienceRestriction(request.Realm)
            });

            conditions.NotOnOrAfter = now.AddMinutes(
                rp.TokenLifetimeInMinutes.GetValueOrDefault(_options.DefaultNotOnOrAfterInMinutes));
            conditions.NotBefore = now.Subtract(TimeSpan.FromMinutes(_options.DefaultNotBeforeInMinutes));

            var authContext = new Saml2AuthenticationContext(new Uri("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"));

            var authStatement = new Saml2AuthenticationStatement(authContext, now);

            authStatement.SessionIndex = (request.Parameters.ContainsKey("SessionIndex")) ? request.Parameters["SessionIndex"] : null;

            var attributeStament = new Saml2AttributeStatement();

            foreach (var claim in outgoingSubject.Claims)
            {
                _logger.LogDebug("Adding attribute in SAML token '{0} - {1}'", claim.Type, claim.Value);

                attributeStament.Attributes.Add(new Saml2Attribute(claim.Type, claim.Value));
            }

            var assertion = new Saml2Assertion(issuer);

            assertion.Id         = new Saml2Id();
            assertion.Subject    = subject;
            assertion.Conditions = conditions;
            assertion.Statements.Add(attributeStament);
            assertion.Statements.Add(authStatement);
            assertion.IssueInstant = now;

            assertion.SigningCredentials = await _keyService.GetSigningCredentialsAsync();

            var token = new Saml2SecurityToken(assertion);

            token.SigningKey = assertion.SigningCredentials.Key;

            return(token);
        }