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)); }
/// <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); }
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); }
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); }
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); }
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); }
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); }
/// <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); }
/// <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); }
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); }