//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.IdentityModel { using System; using System.IdentityModel.Tokens; using System.Security.Claims; using RST = System.IdentityModel.Protocols.WSTrust.RequestSecurityToken; using RSTR = System.IdentityModel.Protocols.WSTrust.RequestSecurityTokenResponse; using System.IdentityModel.Protocols.WSTrust; using System.IdentityModel.Configuration; /// /// Abstract class for building WS-Security token services. /// public abstract class SecurityTokenService { /// /// This class is used to maintain request state across asynchronous calls /// within the security token service. /// protected class FederatedAsyncState { RST _request; ClaimsPrincipal _claimsPrincipal; SecurityTokenHandler _securityTokenHandler; IAsyncResult _result; /// /// Copy constructor. /// /// The input FederatedAsyncState instance. /// The input 'FederatedAsyncState' is null. public FederatedAsyncState(FederatedAsyncState federatedAsyncState) { if (null == federatedAsyncState) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("FederatedAsyncState"); } _request = federatedAsyncState.Request; _claimsPrincipal = federatedAsyncState.ClaimsPrincipal; _securityTokenHandler = federatedAsyncState.SecurityTokenHandler; _result = federatedAsyncState.Result; } /// /// Constructs a FederatedAsyncState instance with token request, principal, and the async result. /// /// The token request instance. /// The identity of the token requestor. /// The async result. /// When the given request or async result is null. public FederatedAsyncState(RST request, ClaimsPrincipal principal, IAsyncResult result) { if (null == request) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } if (null == result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); } _request = request; _claimsPrincipal = principal; _result = result; } /// /// Gets the token request instance. /// public RST Request { get { return _request; } } /// /// Gets the ClaimsPrincipal instance. /// public ClaimsPrincipal ClaimsPrincipal { get { return _claimsPrincipal; } } /// /// Gets or sets the SecurityTokenHandler to be used during an async token-issuance call. /// public SecurityTokenHandler SecurityTokenHandler { get { return _securityTokenHandler; } set { _securityTokenHandler = value; } } /// /// Gets the async result. /// public IAsyncResult Result { get { return _result; } } } // // STS settings // SecurityTokenServiceConfiguration _securityTokenServiceConfiguration; ClaimsPrincipal _principal; RequestSecurityToken _request; SecurityTokenDescriptor _tokenDescriptor; /// /// Use this constructor to initialize scope provider and token issuer certificate. /// /// The SecurityTokenServiceConfiguration that will have the related settings for the STS. protected SecurityTokenService(SecurityTokenServiceConfiguration securityTokenServiceConfiguration) { if (securityTokenServiceConfiguration == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenServiceConfiguration"); } _securityTokenServiceConfiguration = securityTokenServiceConfiguration; } /// /// Async Cancel. /// /// The identity of the token requestor. /// The security token request which includes request message as well as other client /// related information such as authorization context. /// The async call back. /// The state object. /// The async result. public virtual IAsyncResult BeginCancel(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null ? request.RequestType : "Cancel")))); } /// /// Begins async call for GetScope routine. Default implementation will throw a NotImplementedExcetion. /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. /// /// The identity of the token requestor. /// The request. /// The callback to be invoked when the user Asynchronous operation completed. /// The state object. /// IAsyncResult. Represents the status of an asynchronous operation. This will be passed into /// EndGetScope. protected virtual IAsyncResult BeginGetScope(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); } /// /// Begins the async call of the Issue request. /// /// The to issue a token for. /// The security token request which includes request message as well as other client /// related information such as authorization context. /// The async call back. /// The state object. /// The async result. public virtual IAsyncResult BeginIssue(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } _principal = principal; _request = request; // // Step 1: Validate the rst: check if this STS is capable of handling this // rst // ValidateRequest(request); // // FederatedAsyncState asyncState = new FederatedAsyncState(request, principal, new TypedAsyncResult(callback, state)); BeginGetScope(principal, request, OnGetScopeComplete, asyncState); return asyncState.Result; } /// /// Async Renew. /// /// The to renew. /// The security token request which includes request message as well as other client /// related information such as authorization context. /// The async call back. /// The state object. /// The async result. public virtual IAsyncResult BeginRenew(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Renew")))); } /// /// Async Validate. /// /// The to validate. /// The security token request which includes request message as well as other client /// related information such as authorization context. /// The async call back. /// The state object. /// The async result. public virtual IAsyncResult BeginValidate(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Validate")))); } /// /// Cancel. /// /// The to cancel. /// The request. /// The response. public virtual RSTR Cancel(ClaimsPrincipal principal, RST request) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Cancel")))); } /// /// Creates an instance of a . /// /// The incoming token request. /// The object returned from . /// The . /// Invoked during token issuance after . protected virtual SecurityTokenDescriptor CreateSecurityTokenDescriptor(RST request, Scope scope) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } if (scope == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("scope"); } SecurityTokenDescriptor d = new SecurityTokenDescriptor(); d.AppliesToAddress = scope.AppliesToAddress; d.ReplyToAddress = scope.ReplyToAddress; d.SigningCredentials = scope.SigningCredentials; if (null == d.SigningCredentials) { d.SigningCredentials = this.SecurityTokenServiceConfiguration.SigningCredentials; } // // The encrypting credentials specified on the Scope object // are invariant relative to a specific RP. Allowing the STS to // cache the Scope for each RP. // Our default implementation will generate the symmetric bulk // encryption key on the fly. // if (scope.EncryptingCredentials != null && scope.EncryptingCredentials.SecurityKey is AsymmetricSecurityKey ) { if ((request.EncryptionAlgorithm == null || request.EncryptionAlgorithm == SecurityAlgorithms.Aes256Encryption) && (request.SecondaryParameters == null || request.SecondaryParameters.EncryptionAlgorithm == null || request.SecondaryParameters.EncryptionAlgorithm == SecurityAlgorithms.Aes256Encryption) ) { d.EncryptingCredentials = new EncryptedKeyEncryptingCredentials(scope.EncryptingCredentials, 256, SecurityAlgorithms.Aes256Encryption); } } return d; } /// /// Gets the STS's name. /// /// Returns the issuer name. protected virtual string GetIssuerName() { return SecurityTokenServiceConfiguration.TokenIssuerName; } /// /// Checks the IssuerName for validity (non-null) /// private string GetValidIssuerName() { string issuerName = GetIssuerName(); if (string.IsNullOrEmpty(issuerName)) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2083)); } return issuerName; } /// /// Gets the proof token. /// /// The incoming token request. /// The scope instance encapsulating information about the relying party. /// The newly created proof decriptor that could be either asymmetric proof descriptor or symmetric proof descriptor or null in the bearer token case. protected virtual ProofDescriptor GetProofToken(RST request, Scope scope) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } if (scope == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("scope"); } EncryptingCredentials requestorWrappingCredentials = GetRequestorProofEncryptingCredentials(request); if (scope.EncryptingCredentials != null && !(scope.EncryptingCredentials.SecurityKey is AsymmetricSecurityKey)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityTokenException(SR.GetString(SR.ID4179))); } EncryptingCredentials targetWrappingCredentials = scope.EncryptingCredentials; // // Generate the proof key // string keyType = (string.IsNullOrEmpty(request.KeyType)) ? KeyTypes.Symmetric : request.KeyType; ProofDescriptor result = null; if (StringComparer.Ordinal.Equals(keyType, KeyTypes.Asymmetric)) { // // Asymmetric is only supported with UseKey // if (request.UseKey == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3091))); } result = new AsymmetricProofDescriptor(request.UseKey.SecurityKeyIdentifier); } else if (StringComparer.Ordinal.Equals(keyType, KeyTypes.Symmetric)) { // // Only support PSHA1. Overwrite STS to support custom key algorithm // if (request.ComputedKeyAlgorithm != null && !StringComparer.Ordinal.Equals(request.ComputedKeyAlgorithm, ComputedKeyAlgorithms.Psha1)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID2011, request.ComputedKeyAlgorithm))); } // // We must wrap the symmetric key inside the security token // if (targetWrappingCredentials == null && scope.SymmetricKeyEncryptionRequired) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID4007))); } // // We are encrypting the proof token or the server entropy using client's encrypting credential if present, // which will be used to encrypt the key during serialization. // Otherwise, we can only send back the key in plain text. However, the current implementation of // WSTrustServiceContract sets the rst.ProofEncryption = null by default. Therefore, the server entropy // or the proof token will be sent in plain text no matter the client's entropy is sent encrypted or unencrypted. // if (request.KeySizeInBits.HasValue) { if (request.Entropy != null) { result = new SymmetricProofDescriptor(request.KeySizeInBits.Value, targetWrappingCredentials, requestorWrappingCredentials, request.Entropy.GetKeyBytes(), request.EncryptWith); } else { result = new SymmetricProofDescriptor(request.KeySizeInBits.Value, targetWrappingCredentials, requestorWrappingCredentials, request.EncryptWith); } } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID2059))); } } else if (StringComparer.Ordinal.Equals(keyType, KeyTypes.Bearer)) { // // Intentionally empty, no proofDescriptor // } return result; } /// /// Get the Requestor's Proof encrypting credentials. /// /// RequestSecurityToken /// EncryptingCredentials /// Input argument 'request' is null. protected virtual EncryptingCredentials GetRequestorProofEncryptingCredentials(RST request) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } if (request.ProofEncryption == null) { return null; } X509SecurityToken x509SecurityToken = request.ProofEncryption.GetSecurityToken() as X509SecurityToken; if (x509SecurityToken != null) { return new X509EncryptingCredentials(x509SecurityToken); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID2084, request.ProofEncryption.GetSecurityToken()))); } /// /// Gets the lifetime of the issued token. /// Normally called with the lifetime that arrived in the RST. /// The algorithm for calculating the token lifetime is: /// requestLifeTime (in) LifeTime (returned) /// Created Expires Created Expires /// null null DateTime.UtcNow DateTime.UtcNow + SecurityTokenServiceConfiguration.DefaultTokenLifetime /// C null C C + SecurityTokenServiceConfiguration.DefaultTokenLifetime /// null E DateTime.UtcNow E /// C E C E /// /// The requestor's desired life time. protected virtual Lifetime GetTokenLifetime(Lifetime requestLifetime) { DateTime created; DateTime expires; if (requestLifetime == null) { created = DateTime.UtcNow; expires = DateTimeUtil.Add(created, _securityTokenServiceConfiguration.DefaultTokenLifetime); } else { if (requestLifetime.Created.HasValue) { created = requestLifetime.Created.Value; } else { created = DateTime.UtcNow; } if (requestLifetime.Expires.HasValue) { expires = requestLifetime.Expires.Value; } else { expires = DateTimeUtil.Add(created, _securityTokenServiceConfiguration.DefaultTokenLifetime); } } VerifyComputedLifetime(created, expires); return new Lifetime(created, expires); } private void VerifyComputedLifetime(DateTime created, DateTime expires) { DateTime utcNow = DateTime.UtcNow; // if expires in past, throw if (DateTimeUtil.Add(DateTimeUtil.ToUniversalTime(expires), _securityTokenServiceConfiguration.MaxClockSkew) < utcNow) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2075, created, expires, utcNow))); } // if creation time specified is greater than one day in future, throw if (DateTimeUtil.ToUniversalTime(created) > DateTimeUtil.Add(utcNow + TimeSpan.FromDays(1), _securityTokenServiceConfiguration.MaxClockSkew)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2076, created, expires, utcNow))); } // if expiration time is equal to or before creation time, throw. This would be hard to make happen as the Lifetime class checks this condition in the constructor if (expires <= created) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2077, created, expires))); } // if timespan is greater than allowed, throw if ((expires - created) > _securityTokenServiceConfiguration.MaximumTokenLifetime) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2078, created, expires, _securityTokenServiceConfiguration.MaximumTokenLifetime))); } return; } /// /// Creates the RSTR and finally read the information from TokenDescriptor and apply /// those to the RSTR. /// /// The RST from the request. /// The token descriptor which contains the information for the issued token. /// The RSTR for the response, null if the token descriptor is null. protected virtual RSTR GetResponse(RST request, SecurityTokenDescriptor tokenDescriptor) { if (tokenDescriptor != null) { RSTR rstr = new RSTR(request); tokenDescriptor.ApplyTo(rstr); // Set the replyTo address of the relying party (if any) in the outgoing RSTR from the generated // token descriptor (STD) based on the table below: // // RST.ReplyTo STD.ReplyToAddress RSTR.ReplyTo // =========== ==================== ============ // Set Not Set Not Set // Set Set Set to STD.ReplyToAddress // Not Set Not Set Not Set // Not Set Set Not Set // if (request.ReplyTo != null) { rstr.ReplyTo = tokenDescriptor.ReplyToAddress; } // // Set the appliesTo address (if any) in the outgoing RSTR from the generated token descriptor. // if (!string.IsNullOrEmpty(tokenDescriptor.AppliesToAddress)) { rstr.AppliesTo = new EndpointReference(tokenDescriptor.AppliesToAddress); } return rstr; } else { return null; } } /// /// Async Cancel. /// /// /// public virtual RSTR EndCancel(IAsyncResult result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, "Cancel"))); } /// /// Ends the Async call to BeginGetScope. Default implementation will throw a NotImplementedException. /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. /// /// Typed Async result which contains the result. This is the same instance of /// IAsyncResult that was returned by the BeginGetScope method. /// The scope. protected virtual Scope EndGetScope(IAsyncResult result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); } /// /// Ends the async call of Issue request. This would finally return the RequestSecurityTokenResponse. /// /// The async result returned from the BeginIssue method. /// The security token response. public virtual RSTR EndIssue(IAsyncResult result) { if (result == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); } if (!(result is TypedAsyncResult)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2012, typeof(TypedAsyncResult), result.GetType()))); } return TypedAsyncResult.End(result); } /// /// Async Renew. /// /// /// public virtual RSTR EndRenew(IAsyncResult result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, "Renew"))); } /// /// Async Validate. /// /// /// public virtual RSTR EndValidate(IAsyncResult result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, "Validate"))); } /// /// Retrieves the relying party information. Override this method to provide a custom scope with /// relying party related information. /// /// The identity of the token requestor. /// The request message. /// A scope object based on the relying party related information. protected abstract Scope GetScope(ClaimsPrincipal principal, RST request); /// /// When overridden in a derived class, this method should return a collection of output subjects to be included in the issued token. /// /// The ClaimsPrincipal that represents the identity of the requestor. /// The token request parameters that arrived in the call. /// The scope information about the Relying Party. /// The ClaimsIdentity representing the collection of claims that will be placed in the issued security token. protected abstract ClaimsIdentity GetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope); /// /// Begins async call for GetOutputSubjects routine. Default implementation will throw a NotImplementedExcetion. /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. /// /// The authorization context that represents the identity of the requestor. /// The token request parameters that arrived in the call. /// The scope information about the Relying Party. /// The callback to be invoked when the user Asynchronous operation completed. /// The state object. /// IAsyncResult. Represents the status of an asynchronous operation. This will be passed into /// EndGetOutputClaimsIdentity. protected virtual IAsyncResult BeginGetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope, AsyncCallback callback, object state) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); } /// /// Ends the Async call to BeginGetOutputSubjects. Default implementation will throw a NotImplementedExcetion. /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. /// /// Typed Async result which contains the result. This was the same IAsyncResult that /// was returned by the BeginGetOutputClaimsIdentity. /// The claimsets collection that will be placed inside the issued token. protected virtual ClaimsIdentity EndGetOutputClaimsIdentity(IAsyncResult result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); } /// /// Issues a Security Token. /// /// The identity of the token requestor. /// The request. /// The response. public virtual RSTR Issue(ClaimsPrincipal principal, RST request) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } _principal = principal; _request = request; // 1. Do request validation ValidateRequest(request); // 2. Get the scope and populate into tokenDescriptor Scope scope = GetScope(principal, request); if (scope == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2013)); } this.Scope = scope; // Create the security token descriptor now that we have a scope. this.SecurityTokenDescriptor = CreateSecurityTokenDescriptor(request, scope); if (this.SecurityTokenDescriptor == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2003)); } if (this.SecurityTokenDescriptor.SigningCredentials == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2079)); } // // If TokenEncryptionRequired is set to true, then we must encrypt the token. // if (this.Scope.TokenEncryptionRequired && this.SecurityTokenDescriptor.EncryptingCredentials == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4184)); } // 3. Get the token-handler SecurityTokenHandler securityTokenHandler = GetSecurityTokenHandler(request.TokenType); if (securityTokenHandler == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID4010, request.TokenType))); } // 4. Get the issuer name and populate into tokenDescriptor _tokenDescriptor.TokenIssuerName = GetValidIssuerName(); // 5. Get the token lifetime and populate into tokenDescriptor _tokenDescriptor.Lifetime = GetTokenLifetime(request.Lifetime); // 6. Get the proof token and populate into tokenDescriptor _tokenDescriptor.Proof = GetProofToken(request, scope); // 7. Get the subjects and populate into tokenDescriptor _tokenDescriptor.Subject = GetOutputClaimsIdentity(principal, request, scope); // use the securityTokenHandler from Step 3 to create and setup the issued token information on the tokenDescriptor // (actual issued token, AttachedReference and UnattachedReference) // TokenType is preserved from the request if possible if (!string.IsNullOrEmpty(request.TokenType)) { _tokenDescriptor.TokenType = request.TokenType; } else { string[] identifiers = securityTokenHandler.GetTokenTypeIdentifiers(); if (identifiers == null || identifiers.Length == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4264, request.TokenType))); } _tokenDescriptor.TokenType = identifiers[0]; } _tokenDescriptor.Token = securityTokenHandler.CreateToken(_tokenDescriptor); _tokenDescriptor.AttachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, true); _tokenDescriptor.UnattachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, false); // 9. Create the RSTR RSTR rstr = GetResponse(request, _tokenDescriptor); return rstr; } /// /// Gets an appropriate SecurityTokenHandler for issuing a security token. /// /// The requested TokenType. /// The SecurityTokenHandler to be used for creating the issued security token. protected virtual SecurityTokenHandler GetSecurityTokenHandler(string requestedTokenType) { string tokenType = string.IsNullOrEmpty(requestedTokenType) ? _securityTokenServiceConfiguration.DefaultTokenType : requestedTokenType; SecurityTokenHandler securityTokenHandler = _securityTokenServiceConfiguration.SecurityTokenHandlers[tokenType]; return securityTokenHandler; } /// /// This routine processes the stored data about the target resource /// for who the Issued token is for. /// /// The async result. /// When the given async result is null. void OnGetScopeComplete(IAsyncResult result) { if (null == result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); } FederatedAsyncState state = result.AsyncState as FederatedAsyncState; if (state == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2001))); } Exception unhandledException = null; TypedAsyncResult typedResult = state.Result as TypedAsyncResult; if (typedResult == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2004, typeof(TypedAsyncResult), state.Result.GetType()))); } RST request = state.Request; try { // // 2. Retrieve the scope information // Scope scope = EndGetScope(result); if (scope == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2013)); } this.Scope = scope; // // Create a security token descriptor // this.SecurityTokenDescriptor = CreateSecurityTokenDescriptor(request, this.Scope); if (this.SecurityTokenDescriptor == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2003)); } if (this.SecurityTokenDescriptor.SigningCredentials == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2079)); } // // If TokenEncryptionRequired is set to true, then we must encrypt the token. // if (this.Scope.TokenEncryptionRequired && this.SecurityTokenDescriptor.EncryptingCredentials == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4184)); } // // Step 3: Retrieve the token handler to use for creating token and store it in the state // SecurityTokenHandler securityTokenHandler = GetSecurityTokenHandler(request == null ? null : request.TokenType); if (securityTokenHandler == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID4010, request == null ? String.Empty : request.TokenType))); } state.SecurityTokenHandler = securityTokenHandler; // // Step 4: Logical issuer name // _tokenDescriptor.TokenIssuerName = GetValidIssuerName(); // // Step 5: Establish token lifetime // _tokenDescriptor.Lifetime = GetTokenLifetime(request == null ? null : request.Lifetime); // // Step 6: Compute the proof key // _tokenDescriptor.Proof = GetProofToken(request, this.Scope); // // Start the async call for generating the output subjects. // BeginGetOutputClaimsIdentity(state.ClaimsPrincipal, state.Request, scope, OnGetOutputClaimsIdentityComplete, state); } #pragma warning suppress 56500 catch (Exception e) { if (System.Runtime.Fx.IsFatal(e)) throw; unhandledException = e; } if (unhandledException != null) { // // Complete the request in failure // typedResult.Complete(null, result.CompletedSynchronously, unhandledException); } } /// /// The callback that is invoked on the completion of the BeginGetOutputSubjects call. /// /// The async result. /// When the given async result is null. void OnGetOutputClaimsIdentityComplete(IAsyncResult result) { if (null == result) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); } FederatedAsyncState state = result.AsyncState as FederatedAsyncState; if (state == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2001))); } SecurityTokenHandler securityTokenHandler = state.SecurityTokenHandler; if (securityTokenHandler == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2016))); } Exception unhandledException = null; RST request = state.Request; RSTR response = null; TypedAsyncResult typedResult = state.Result as TypedAsyncResult; if (typedResult == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2004, typeof(TypedAsyncResult), state.Result.GetType()))); } try { // // get token descriptor // if (_tokenDescriptor == null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2003)); } // Step 7: Retrieve the output claims to be included in the issued-token _tokenDescriptor.Subject = EndGetOutputClaimsIdentity(result); // // Use the retrieved securityTokenHandler to create and setup the issued token information on the tokenDescriptor // (actual issued token, AttachedReference and UnattachedReference) // TokenType is preserved from the request if possible if (!string.IsNullOrEmpty(request.TokenType)) { _tokenDescriptor.TokenType = request.TokenType; } else { string[] identifiers = securityTokenHandler.GetTokenTypeIdentifiers(); if (identifiers == null || identifiers.Length == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4264, request.TokenType))); } _tokenDescriptor.TokenType = identifiers[0]; } _tokenDescriptor.Token = securityTokenHandler.CreateToken(_tokenDescriptor); _tokenDescriptor.AttachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, true); _tokenDescriptor.UnattachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, false); // 9. Create the RSTR response = GetResponse(request, _tokenDescriptor); } #pragma warning suppress 56500 catch (Exception e) { if (System.Runtime.Fx.IsFatal(e)) throw; unhandledException = e; } typedResult.Complete(response, typedResult.CompletedSynchronously, unhandledException); } /// /// Gets the Owner configuration instance. /// public SecurityTokenServiceConfiguration SecurityTokenServiceConfiguration { get { return _securityTokenServiceConfiguration; } } /// /// Gets or sets the ClaimsPrincipal associated with the current instance. /// public ClaimsPrincipal Principal { get { return _principal; } set { _principal = value; } } /// /// Gets or sets the RequestSecurityToken associated with the current instance. /// public RequestSecurityToken Request { get { return _request; } set { _request = value; } } /// /// Gets or sets the Scope associated with the current instance. /// public Scope Scope { get; set; } /// /// Gets or sets the SecurityTokenDescriptor associated with the current instance. /// protected SecurityTokenDescriptor SecurityTokenDescriptor { get { return _tokenDescriptor; } set { if (value == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); } _tokenDescriptor = value; } } /// /// Renew. /// /// The to renew. /// The request. /// The response. public virtual RSTR Renew(ClaimsPrincipal principal, RST request) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Renew")))); } /// /// Validate. /// /// The to validate. /// The request. /// The response. public virtual RSTR Validate(ClaimsPrincipal principal, RST request) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Validate")))); } /// /// Validates the RequestSecurityToken encapsulated by this SecurityTokenService instance. /// protected virtual void ValidateRequest(RST request) { // currently we only support RST/RSTR pattern if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2051))); } // STS only support Issue for now if (request.RequestType != null && request.RequestType != RequestTypes.Issue) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2052))); } // key type must be one of the known types if (request.KeyType != null && !IsKnownType(request.KeyType)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2053))); } // if key type is bearer key, we should fault if the KeySize element is present and its value is not equal to zero. if (StringComparer.Ordinal.Equals(request.KeyType, KeyTypes.Bearer) && request.KeySizeInBits.HasValue && (request.KeySizeInBits.Value != 0)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2050))); } // token type must be supported for this STS if (GetSecurityTokenHandler(request.TokenType) == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UnsupportedTokenTypeBadRequestException(request.TokenType)); } request.KeyType = (string.IsNullOrEmpty(request.KeyType)) ? KeyTypes.Symmetric : request.KeyType; if (StringComparer.Ordinal.Equals(request.KeyType, KeyTypes.Symmetric)) { // // Check if the key size is within certain limit to prevent Dos attack // if (request.KeySizeInBits.HasValue) { if (request.KeySizeInBits.Value > _securityTokenServiceConfiguration.DefaultMaxSymmetricKeySizeInBits) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2056, request.KeySizeInBits.Value, _securityTokenServiceConfiguration.DefaultMaxSymmetricKeySizeInBits))); } } else { request.KeySizeInBits = _securityTokenServiceConfiguration.DefaultSymmetricKeySizeInBits; } } } static bool IsKnownType(string keyType) { return (StringComparer.Ordinal.Equals(keyType, KeyTypes.Symmetric) || StringComparer.Ordinal.Equals(keyType, KeyTypes.Asymmetric) || StringComparer.Ordinal.Equals(keyType, KeyTypes.Bearer)); } } }