//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth2.ChannelElements { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Security.Cryptography; using System.Text; using DotNetOpenAuth.Messaging; /// /// Represents the authorization code created when a user approves authorization that /// allows the client to request an access/refresh token. /// internal class AuthorizationCode : AuthorizationDataBag { /// /// The name of the bucket for symmetric keys used to sign authorization codes. /// internal const string AuthorizationCodeKeyBucket = "https://localhost/dnoa/oauth_authorization_code"; /// /// Initializes a new instance of the class. /// public AuthorizationCode() { } /// /// Initializes a new instance of the class. /// /// The client identifier. /// The callback the client used to obtain authorization, if one was explicitly included in the request. /// The authorized scopes. /// The name on the account that authorized access. internal AuthorizationCode(string clientIdentifier, Uri callback, IEnumerable scopes, string username) { Requires.NotNullOrEmpty(clientIdentifier, "clientIdentifier"); this.ClientIdentifier = clientIdentifier; this.CallbackHash = CalculateCallbackHash(callback); this.Scope.ResetContents(scopes); this.User = username; this.UtcCreationDate = DateTime.UtcNow; } /// /// Gets or sets the hash of the callback URL. /// [MessagePart("cb")] private byte[] CallbackHash { get; set; } /// /// Creates a serializer/deserializer for this type. /// /// The authorization server that will be serializing/deserializing this authorization code. Must not be null. /// A DataBag formatter. internal static IDataBagFormatter CreateFormatter(IAuthorizationServer authorizationServer) { Requires.NotNull(authorizationServer, "authorizationServer"); Contract.Ensures(Contract.Result>() != null); return new UriStyleMessageFormatter( authorizationServer.CryptoKeyStore, AuthorizationCodeKeyBucket, signed: true, encrypted: true, compressed: false, maximumAge: AuthorizationCodeBindingElement.MaximumMessageAge, decodeOnceOnly: authorizationServer.VerificationCodeNonceStore); } /// /// Verifies the the given callback URL matches the callback originally given in the authorization request. /// /// The callback. /// /// This method serves to verify that the callback URL given in the original authorization request /// and the callback URL given in the access token request match. /// /// Thrown when the callback URLs do not match. [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "redirecturimismatch", Justification = "Protocol requirement")] [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "DotNetOpenAuth.Messaging.ErrorUtilities.VerifyProtocol(System.Boolean,System.String,System.Object[])", Justification = "Protocol requirement")] internal void VerifyCallback(Uri callback) { ErrorUtilities.VerifyProtocol(MessagingUtilities.AreEquivalent(this.CallbackHash, CalculateCallbackHash(callback)), Protocol.redirect_uri_mismatch); } /// /// Calculates the hash of the callback URL. /// /// The callback whose hash should be calculated. /// /// A base64 encoding of the hash of the URL. /// [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "False positive.")] private static byte[] CalculateCallbackHash(Uri callback) { if (callback == null) { return null; } using (var hasher = new SHA256Managed()) { return hasher.ComputeHash(Encoding.UTF8.GetBytes(callback.AbsoluteUri)); } } } }