Bewerken

Delen via


SecurityTokenHandler.ValidateToken(SecurityToken) Method

Definition

When overridden in a derived class, validates the specified security token. The token must be of the type processed by the derived class.

public:
 virtual System::Collections::ObjectModel::ReadOnlyCollection<System::Security::Claims::ClaimsIdentity ^> ^ ValidateToken(System::IdentityModel::Tokens::SecurityToken ^ token);
public virtual System.Collections.ObjectModel.ReadOnlyCollection<System.Security.Claims.ClaimsIdentity> ValidateToken (System.IdentityModel.Tokens.SecurityToken token);
abstract member ValidateToken : System.IdentityModel.Tokens.SecurityToken -> System.Collections.ObjectModel.ReadOnlyCollection<System.Security.Claims.ClaimsIdentity>
override this.ValidateToken : System.IdentityModel.Tokens.SecurityToken -> System.Collections.ObjectModel.ReadOnlyCollection<System.Security.Claims.ClaimsIdentity>
Public Overridable Function ValidateToken (token As SecurityToken) As ReadOnlyCollection(Of ClaimsIdentity)

Parameters

token
SecurityToken

The token to validate.

Returns

The identities contained in the token.

Examples

The following code shows an override of the ValidateToken method for a security token handler that processes simple web tokens (SWT). The code is taken from the CustomToken sample. For information about this sample and other samples available for WIF and where to download them, see WIF Code Sample Index.

/// <summary>
/// This method validates the Simple Web Token.
/// </summary>
/// <param name="token">A simple web token.</param>
/// <returns>A Claims Collection which contains all the claims from the token.</returns>
public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
{
    if ( token == null )
    {
        throw new ArgumentNullException( "token" );
    }

    SimpleWebToken simpleWebToken = token as SimpleWebToken;
    if ( simpleWebToken == null )
    {
        throw new ArgumentException("The token provided must be of type SimpleWebToken.");                    
    }

    if ( DateTime.Compare( simpleWebToken.ValidTo.Add( Configuration.MaxClockSkew ), DateTime.UtcNow ) <= 0 )
    {
        throw new SecurityTokenExpiredException("The incoming token has expired. Get a new access token from the Authorization Server.");
    }

    ValidateSignature( simpleWebToken );
 
    ValidateAudience( simpleWebToken.Audience );
 
    ClaimsIdentity claimsIdentity = CreateClaims( simpleWebToken );
   
    if (this.Configuration.SaveBootstrapContext)
    {
        claimsIdentity.BootstrapContext = new BootstrapContext(simpleWebToken.SerializedToken);
    }

    List<ClaimsIdentity> claimCollection = new List<ClaimsIdentity>(new ClaimsIdentity[] { claimsIdentity });
    return claimCollection.AsReadOnly();
}

The following code shows CreateClaims method that is invoked from the override of the ValidateToken method in the previous example. This method returns a ClaimsIdentity object that is created from the claims in the token. The code is taken from the CustomToken sample. For information about this sample and other samples available for WIF and where to download them, see WIF Code Sample Index.

/// <summary>Creates <see cref="Claim"/>'s from the incoming token.
/// </summary>
/// <param name="simpleWebToken">The incoming <see cref="SimpleWebToken"/>.</param>
/// <returns>A <see cref="ClaimsIdentity"/> created from the token.</returns>
protected virtual ClaimsIdentity CreateClaims( SimpleWebToken simpleWebToken )
{
    if ( simpleWebToken == null )
    {
        throw new ArgumentNullException( "simpleWebToken" );
    }

    NameValueCollection tokenProperties = simpleWebToken.GetAllProperties();
    if ( tokenProperties == null )
    {
        throw new SecurityTokenValidationException( "No claims can be created from this Simple Web Token." );
    }

    if ( Configuration.IssuerNameRegistry == null )
    {
        throw new InvalidOperationException( "The Configuration.IssuerNameRegistry property of this SecurityTokenHandler is set to null. Tokens cannot be validated in this state." );
    }

    string normalizedIssuer = Configuration.IssuerNameRegistry.GetIssuerName( simpleWebToken );

    ClaimsIdentity identity = new ClaimsIdentity(AuthenticationTypes.Federation);
    
    foreach ( string key in tokenProperties.Keys )
    {
        if ( ! IsReservedKeyName(key) &&  !string.IsNullOrEmpty( tokenProperties[key] ) )
        {
            identity.AddClaim( new Claim( key, tokenProperties[key], ClaimValueTypes.String, normalizedIssuer ) );
            if ( key == AcsNameClaimType )
            {
                // add a default name claim from the Name identifier claim.
                identity.AddClaim( new Claim( DefaultNameClaimType, tokenProperties[key], ClaimValueTypes.String, normalizedIssuer ) );
            }
        }
    }

    return identity;
}

The following code shows ValidateSignature method that is invoked from the override of the ValidateToken method in the simple web token handler. This method validates the signature on the token by using the configured IssuerTokenResolver. The code is taken from the CustomToken sample. For information about this sample and other samples available for WIF and where to download them, see WIF Code Sample Index.

/// <summary>
/// Validates the signature on the incoming token.
/// </summary>
/// <param name="simpleWebToken">The incoming <see cref="SimpleWebToken"/>.</param>
protected virtual void ValidateSignature( SimpleWebToken simpleWebToken )
{
    if ( simpleWebToken == null )
    {
        throw new ArgumentNullException( "simpleWebToken" );
    }

    if ( String.IsNullOrEmpty( simpleWebToken.SerializedToken ) || String.IsNullOrEmpty( simpleWebToken.Signature ) )
    {
        throw new SecurityTokenValidationException( "The token does not have a signature to verify" );
    }

    string serializedToken = simpleWebToken.SerializedToken;           
    string unsignedToken = null;

    // Find the last parameter. The signature must be last per SWT specification.
    int lastSeparator = serializedToken.LastIndexOf( ParameterSeparator );

    // Check whether the last parameter is an hmac.
    if ( lastSeparator > 0 )
    {
        string lastParamStart = ParameterSeparator + SimpleWebTokenConstants.Signature + "=";
        string lastParam = serializedToken.Substring( lastSeparator );

        // Strip the trailing hmac to obtain the original unsigned string for later hmac verification.               
        if ( lastParam.StartsWith( lastParamStart, StringComparison.Ordinal ) )
        {
            unsignedToken = serializedToken.Substring( 0, lastSeparator );
        }
    }

    SimpleWebTokenKeyIdentifierClause clause = new SimpleWebTokenKeyIdentifierClause(simpleWebToken.Audience);
    InMemorySymmetricSecurityKey securityKey = null;
    try
    {
        securityKey = (InMemorySymmetricSecurityKey)this.Configuration.IssuerTokenResolver.ResolveSecurityKey(clause);
    }
    catch (InvalidOperationException)
    {
        throw new SecurityTokenValidationException( "A Symmetric key was not found for the given key identifier clause.");
    }

    string generatedSignature = GenerateSignature( unsignedToken, securityKey.GetSymmetricKey() );

    if ( string.CompareOrdinal( generatedSignature, simpleWebToken.Signature ) != 0 )
    {
        throw new SecurityTokenValidationException( "The signature on the incoming token is invalid.") ;
    }
}
/// <summary>
/// Generates an HMACSHA256 signature for a given string and key.
/// </summary>
/// <param name="unsignedToken">The token to be signed.</param>
/// <param name="signingKey">The key used to generate the signature.</param>
/// <returns>The generated signature.</returns>
protected static string GenerateSignature(string unsignedToken, byte[] signingKey)
{
    using (HMACSHA256 hmac = new HMACSHA256(signingKey))
    {
        byte[] signatureBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(unsignedToken));
        string signature = HttpUtility.UrlEncode(Convert.ToBase64String(signatureBytes));

        return signature;
    }
}

The following code shows ValidateAudience method that is invoked from the override of the ValidateToken method in the simple web token handler. This method validates the audience contained in the token against the audience URIs that were specified in configuration. The code is taken from the CustomToken sample. For information about this sample and other samples available for WIF and where to download them, see WIF Code Sample Index.

/// <summary>
/// Validates the audience of the incoming token with those specified in configuration.
/// </summary>
/// <param name="tokenAudience">The audience of the incoming token.</param>
protected virtual void ValidateAudience( string tokenAudience )
{
    if ( Configuration.AudienceRestriction.AudienceMode != AudienceUriMode.Never )
    {
        if ( String.IsNullOrEmpty( tokenAudience ) )
        {
            throw new SecurityTokenValidationException("The incoming token does not have a valid audience Uri and the Audience Restriction is not set to 'None'.");
        }

        if ( Configuration.AudienceRestriction.AllowedAudienceUris.Count == 0 )
        {
            throw new InvalidOperationException( " Audience Restriction is not set to 'None' but no valid audience URI's are configured." );
        }

        IList<Uri> allowedAudienceUris = Configuration.AudienceRestriction.AllowedAudienceUris;
        
        Uri audienceUri = null;

        Uri.TryCreate(tokenAudience, UriKind.RelativeOrAbsolute, out audienceUri);
        
        // Strip off any query string or fragment. 
        Uri audienceLeftPart;

        if ( audienceUri.IsAbsoluteUri )
        {
            audienceLeftPart = new Uri( audienceUri.GetLeftPart( UriPartial.Path ) );
        }
        else
        {
            Uri baseUri = new Uri( "http://www.example.com" );
            Uri resolved = new Uri( baseUri, tokenAudience );
            audienceLeftPart = baseUri.MakeRelativeUri( new Uri( resolved.GetLeftPart( UriPartial.Path ) ) );
        }

        if ( !allowedAudienceUris.Contains( audienceLeftPart ) )
        {
            throw new AudienceUriValidationFailedException(
                    "The Audience Uri of the incoming token is not present in the list of permitted Audience Uri's.");
        }
    }
}

Remarks

By default this method throws a NotImplementedException exception.

The ValidateToken method is called by the infrastructure to validate and extract the claims from the deserialized security token. These claims are returned in the collection of ClaimsIdentity objects returned by the method. In the typical case, this collection will contain a single identity.

In derived classes, validation typically includes validating the intended audience specified in the token against the audience URIs specified in the SecurityTokenHandlerConfiguration.AudienceRestriction property of the token handler configuration object specified on the Configuration property. These URIs are typically set in the configuration file under the <audienceUris> element. If the audience cannot be validated, an AudienceUriValidationFailedException exception should be thrown.

When processing the token, the issuer is typically validated by passing the issuer token to one of the GetIssuerName methods on the IssuerNameRegistry object that is configured for the handler through the Configuration property. The issuer name registry is typically configured through the <issuerNameRegistry> element in the configuration file. The GetIssuerName returns the name of the issuer. This name should be used to set the Claim.Issuer property in claims contained in the token. If the issuer name registry does not contain an entry for the issuer token, GetIssuerName returns null. In this case a SecurityTokenException is typically thrown in derived classes, but this behavior is up to the designer of the class.

Applies to