diff options
23 files changed, 302 insertions, 585 deletions
diff --git a/projecttemplates/RelyingPartyLogic/Model.Consumer.cs b/projecttemplates/RelyingPartyLogic/Model.Consumer.cs index a09029a..258a248 100644 --- a/projecttemplates/RelyingPartyLogic/Model.Consumer.cs +++ b/projecttemplates/RelyingPartyLogic/Model.Consumer.cs @@ -13,7 +13,7 @@ namespace RelyingPartyLogic { using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; - public partial class Consumer : IConsumerDescription { + public partial class Consumer : IConsumerDescription, DotNetOpenAuth.OAuth2.IConsumerDescription { public VerificationCodeFormat VerificationCodeFormat { get { return (VerificationCodeFormat)this.VerificationCodeFormatAsInt; } set { this.VerificationCodeFormatAsInt = (int)value; } @@ -36,5 +36,16 @@ namespace RelyingPartyLogic { string IConsumerDescription.Key { get { return this.ConsumerKey; } } + + #region IConsumerDescription Members + + /// <summary> + /// Gets the consumer secret. + /// </summary> + string DotNetOpenAuth.OAuth2.IConsumerDescription.Secret { + get { return this.ConsumerSecret; } + } + + #endregion } } diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs index e47e4ee..3700b65 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs @@ -12,9 +12,7 @@ namespace RelyingPartyLogic { using System.Web; using System.Web.Security; using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.OAuth2; public class OAuthAuthenticationModule : IHttpModule { private HttpApplication application; @@ -51,10 +49,13 @@ namespace RelyingPartyLogic { return; } - IDirectedProtocolMessage incomingMessage = OAuthServiceProvider.ServiceProvider.ReadRequest(new HttpRequestInfo(this.application.Context.Request)); - var authorization = incomingMessage as AccessProtectedResourceRequest; - if (authorization != null) { - this.application.Context.User = OAuthServiceProvider.ServiceProvider.CreatePrincipal(authorization); + var tokenAnalyzer = new StandardAccessTokenAnalyzer(OAuthAuthorizationServer.AsymmetricKey, OAuthAuthorizationServer.AsymmetricKey); + var resourceServer = new ResourceServer(tokenAnalyzer); + + IPrincipal principal; + var errorMessage = resourceServer.VerifyAccess(new HttpRequestInfo(this.application.Context.Request), out principal); + if (errorMessage == null) { + this.application.Context.User = principal; } } @@ -70,7 +71,7 @@ namespace RelyingPartyLogic { /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Web.Security.RoleManagerEventArgs"/> instance containing the event data.</param> private void roleManager_GetRoles(object sender, RoleManagerEventArgs e) { - if (this.application.User is OAuthPrincipal) { + if (this.application.User is DotNetOpenAuth.OAuth.ChannelElements.OAuthPrincipal) { e.RolesPopulated = true; } } diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs index 35af472..f4e27a4 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs @@ -15,6 +15,7 @@ namespace RelyingPartyLogic { using System.ServiceModel.Security; using DotNetOpenAuth; using DotNetOpenAuth.OAuth; + using DotNetOpenAuth.OAuth2; /// <summary> /// A WCF extension to authenticate incoming messages using OAuth. @@ -28,15 +29,16 @@ namespace RelyingPartyLogic { return false; } - HttpRequestMessageProperty httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; - Uri requestUri = operationContext.RequestContext.RequestMessage.Properties["OriginalHttpRequestUri"] as Uri; - ServiceProvider sp = OAuthServiceProvider.ServiceProvider; - try { - var auth = sp.ReadProtectedResourceAuthorization(httpDetails, requestUri); - if (auth != null) { - var accessToken = Database.DataContext.IssuedTokens.OfType<IssuedAccessToken>().First(token => token.Token == auth.AccessToken); + var httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; + var requestUri = operationContext.RequestContext.RequestMessage.Properties["OriginalHttpRequestUri"] as Uri; - var principal = sp.CreatePrincipal(auth); + var tokenAnalyzer = new StandardAccessTokenAnalyzer(OAuthAuthorizationServer.AsymmetricKey, OAuthAuthorizationServer.AsymmetricKey); + var resourceServer = new ResourceServer(tokenAnalyzer); + + try { + IPrincipal principal; + var errorResponse = resourceServer.VerifyAccess(httpDetails, requestUri, out principal); + if (errorResponse == null) { var policy = new OAuthPrincipalAuthorizationPolicy(principal); var policies = new List<IAuthorizationPolicy> { policy, @@ -56,8 +58,7 @@ namespace RelyingPartyLogic { }; // Only allow this method call if the access token scope permits it. - string[] scopes = accessToken.Scope.Split('|'); - if (scopes.Contains(operationContext.IncomingMessageHeaders.Action)) { + if (principal.IsInRole(operationContext.IncomingMessageHeaders.Action)) { return true; } } diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs new file mode 100644 index 0000000..ff8bbb4 --- /dev/null +++ b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthAuthorizationServer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace RelyingPartyLogic { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + + using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.OAuth2; + + /// <summary> + /// Provides OAuth 2.0 authorization server information to DotNetOpenAuth. + /// </summary> + public class OAuthAuthorizationServer : IAuthorizationServer { + internal static readonly RSAParameters AsymmetricKey; + + private static readonly byte[] secret; + + private readonly INonceStore nonceStore = new NonceDbStore(); + + static OAuthAuthorizationServer() { + // TODO: Replace this sample code with real code. + // For this sample, we just generate random secrets. + RandomNumberGenerator crypto = new RNGCryptoServiceProvider(); + secret = new byte[16]; + crypto.GetBytes(secret); + + AsymmetricKey = new RSACryptoServiceProvider().ExportParameters(true); + } + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthAuthorizationServer"/> class. + /// </summary> + public OAuthAuthorizationServer() { + } + + #region IAuthorizationServer Members + + /// <summary> + /// Gets the secret used to symmetrically encrypt and sign authorization codes and refresh tokens. + /// </summary> + /// <value></value> + /// <remarks> + /// This secret should be kept strictly confidential in the authorization server(s) + /// and NOT shared with the resource server. Anyone with this secret can mint + /// tokens to essentially grant themselves access to anything they want. + /// </remarks> + public byte[] Secret { + get { return secret; } + } + + /// <summary> + /// Gets the asymmetric private key to use for signing access tokens. + /// </summary> + /// <value></value> + /// <remarks> + /// The public key in the private/public key pair will be used by the resource + /// servers to validate that the access token is minted by a trusted authorization server. + /// </remarks> + public RSAParameters AccessTokenSigningPrivateKey { + get { return AsymmetricKey; } + } + + /// <summary> + /// Gets the authorization code nonce store to use to ensure that authorization codes can only be used once. + /// </summary> + /// <value>The authorization code nonce store.</value> + public INonceStore VerificationCodeNonceStore { + get { return this.nonceStore; } + } + + /// <summary> + /// Gets the client with a given identifier. + /// </summary> + /// <param name="clientIdentifier">The client identifier.</param> + /// <returns>The client registration. Never null.</returns> + /// <exception cref="ArgumentException">Thrown when no client with the given identifier is registered with this authorization server.</exception> + public IConsumerDescription GetClient(string clientIdentifier) { + return Database.DataContext.Consumers.First(c => c.ConsumerKey == clientIdentifier); + } + + /// <summary> + /// Determines whether a described authorization is (still) valid. + /// </summary> + /// <param name="authorization">The authorization.</param> + /// <returns> + /// <c>true</c> if the original authorization is still valid; otherwise, <c>false</c>. + /// </returns> + /// <remarks> + /// <para>When establishing that an authorization is still valid, + /// it's very important to only match on recorded authorizations that + /// meet these criteria:</para> + /// 1) The client identifier matches. + /// 2) The user account matches. + /// 3) The scope on the recorded authorization must include all scopes in the given authorization. + /// 4) The date the recorded authorization was issued must be <em>no later</em> that the date the given authorization was issued. + /// <para>One possible scenario is where the user authorized a client, later revoked authorization, + /// and even later reinstated authorization. This subsequent recorded authorization + /// would not satisfy requirement #4 in the above list. This is important because the revocation + /// the user went through should invalidate all previously issued tokens as a matter of + /// security in the event the user was revoking access in order to sever authorization on a stolen + /// account or piece of hardware in which the tokens were stored. </para> + /// </remarks> + public bool IsAuthorizationValid(DotNetOpenAuth.OAuth2.ChannelElements.IAuthorizationDescription authorization) { + // We don't support revoking tokens yet. + return true; + } + + #endregion + } +} diff --git a/projecttemplates/RelyingPartyLogic/OAuthConsumerTokenManager.cs b/projecttemplates/RelyingPartyLogic/OAuthConsumerTokenManager.cs deleted file mode 100644 index 64e6be8..0000000 --- a/projecttemplates/RelyingPartyLogic/OAuthConsumerTokenManager.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthConsumerTokenManager.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace RelyingPartyLogic { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Web; - using DotNetOpenAuth.OAuth.ChannelElements; - - public class OAuthConsumerTokenManager : OAuthTokenManager, IConsumerTokenManager { - /// <summary> - /// Initializes a new instance of the <see cref="OAuthConsumerTokenManager"/> class. - /// </summary> - /// <param name="consumerKey">The consumer key.</param> - /// <param name="consumerSecret">The consumer secret.</param> - public OAuthConsumerTokenManager(string consumerKey, string consumerSecret) { - if (String.IsNullOrEmpty(consumerKey)) { - throw new ArgumentNullException("consumerKey"); - } - if (consumerSecret == null) { - throw new ArgumentNullException("consumerSecret"); - } - - this.ConsumerKey = consumerKey; - this.ConsumerSecret = consumerSecret; - } - - #region IConsumerTokenManager Members - - /// <summary> - /// Gets the consumer key. - /// </summary> - /// <value>The consumer key.</value> - public string ConsumerKey { get; private set; } - - /// <summary> - /// Gets the consumer secret. - /// </summary> - /// <value>The consumer secret.</value> - public string ConsumerSecret { get; private set; } - - #endregion - } -} diff --git a/projecttemplates/RelyingPartyLogic/OAuthPrincipalAuthorizationPolicy.cs b/projecttemplates/RelyingPartyLogic/OAuthPrincipalAuthorizationPolicy.cs index ddd0b3f..482f44b 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthPrincipalAuthorizationPolicy.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthPrincipalAuthorizationPolicy.cs @@ -10,18 +10,19 @@ namespace RelyingPartyLogic { using System.IdentityModel.Claims; using System.IdentityModel.Policy; using System.Linq; + using System.Security.Principal; using System.Web; using DotNetOpenAuth.OAuth.ChannelElements; public class OAuthPrincipalAuthorizationPolicy : IAuthorizationPolicy { private readonly Guid uniqueId = Guid.NewGuid(); - private readonly OAuthPrincipal principal; + private readonly IPrincipal principal; /// <summary> /// Initializes a new instance of the <see cref="OAuthPrincipalAuthorizationPolicy"/> class. /// </summary> /// <param name="principal">The principal.</param> - public OAuthPrincipalAuthorizationPolicy(OAuthPrincipal principal) { + public OAuthPrincipalAuthorizationPolicy(IPrincipal principal) { this.principal = principal; } diff --git a/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs b/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs index 807da2d..9b6fb50 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs @@ -10,9 +10,9 @@ namespace RelyingPartyLogic { using System.Linq; using System.Web; using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.OAuth2; + using DotNetOpenAuth.OAuth2.ChannelElements; + using DotNetOpenAuth.OAuth2.Messages; public class OAuthServiceProvider { private const string PendingAuthorizationRequestSessionKey = "PendingAuthorizationRequest"; @@ -20,28 +20,26 @@ namespace RelyingPartyLogic { /// <summary> /// The shared service description for this web site. /// </summary> - private static ServiceProviderDescription serviceDescription; - - private static OAuthServiceProviderTokenManager tokenManager; + private static AuthorizationServerDescription authorizationServerDescription; /// <summary> - /// The shared service provider object. + /// The shared authorization server. /// </summary> - private static ServiceProvider serviceProvider; + private static WebServerAuthorizationServer authorizationServer; /// <summary> - /// The lock to synchronize initialization of the <see cref="serviceProvider"/> field. + /// The lock to synchronize initialization of the <see cref="authorizationServer"/> field. /// </summary> - private static object initializerLock = new object(); + private static readonly object InitializerLock = new object(); /// <summary> /// Gets the service provider. /// </summary> /// <value>The service provider.</value> - public static ServiceProvider ServiceProvider { + public static WebServerAuthorizationServer AuthorizationServer { get { EnsureInitialized(); - return serviceProvider; + return authorizationServer; } } @@ -49,83 +47,28 @@ namespace RelyingPartyLogic { /// Gets the service description. /// </summary> /// <value>The service description.</value> - public static ServiceProviderDescription ServiceDescription { + public static AuthorizationServerDescription AuthorizationServerDescription { get { EnsureInitialized(); - return serviceDescription; - } - } - - public static UserAuthorizationRequest PendingAuthorizationRequest { - get { return HttpContext.Current.Session[PendingAuthorizationRequestSessionKey] as UserAuthorizationRequest; } - set { HttpContext.Current.Session[PendingAuthorizationRequestSessionKey] = value; } - } - - public static Consumer PendingAuthorizationConsumer { - get { - ITokenContainingMessage message = PendingAuthorizationRequest; - if (message == null) { - throw new InvalidOperationException(); - } - - return Database.DataContext.IssuedTokens.OfType<IssuedRequestToken>().Include("Consumer").First(t => t.Token == message.Token).Consumer; - } - } - - public static void AuthorizePendingRequestToken() { - var response = AuthorizePendingRequestTokenAndGetResponse(); - if (response != null) { - serviceProvider.Channel.Send(response); - } - } - - public static OutgoingWebResponse AuthorizePendingRequestTokenAsWebResponse() { - var response = AuthorizePendingRequestTokenAndGetResponse(); - if (response != null) { - return serviceProvider.Channel.PrepareResponse(response); - } else { - return null; + return authorizationServerDescription; } } - private static UserAuthorizationResponse AuthorizePendingRequestTokenAndGetResponse() { - var pendingRequest = PendingAuthorizationRequest; - if (pendingRequest == null) { - throw new InvalidOperationException("No pending authorization request to authorize."); - } - - ITokenContainingMessage msg = pendingRequest; - var token = Database.DataContext.IssuedTokens.OfType<IssuedRequestToken>().First(t => t.Token == msg.Token); - token.Authorize(); - - PendingAuthorizationRequest = null; - var response = serviceProvider.PrepareAuthorizationResponse(pendingRequest); - return response; - } - /// <summary> - /// Initializes the <see cref="serviceProvider"/> field if it has not yet been initialized. + /// Initializes the <see cref="authorizationServer"/> field if it has not yet been initialized. /// </summary> private static void EnsureInitialized() { - if (serviceProvider == null) { - lock (initializerLock) { - if (serviceDescription == null) { - var postEndpoint = new MessageReceivingEndpoint(new Uri(Utilities.ApplicationRoot, "OAuth.ashx"), HttpDeliveryMethods.PostRequest); - var getEndpoint = new MessageReceivingEndpoint(postEndpoint.Location, HttpDeliveryMethods.GetRequest); - serviceDescription = new ServiceProviderDescription { - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, - RequestTokenEndpoint = postEndpoint, - AccessTokenEndpoint = postEndpoint, - UserAuthorizationEndpoint = getEndpoint, + if (authorizationServer == null) { + lock (InitializerLock) { + if (authorizationServerDescription == null) { + authorizationServerDescription = new AuthorizationServerDescription { + AuthorizationEndpoint = new Uri(Utilities.ApplicationRoot, "OAuth.ashx"), + TokenEndpoint = new Uri(Utilities.ApplicationRoot, "OAuth.ashx"), }; } - if (tokenManager == null) { - tokenManager = new OAuthServiceProviderTokenManager(); - } - - if (serviceProvider == null) { - serviceProvider = new ServiceProvider(serviceDescription, tokenManager); + if (authorizationServer == null) { + authorizationServer = new WebServerAuthorizationServer(new OAuthAuthorizationServer()); } } } diff --git a/projecttemplates/RelyingPartyLogic/OAuthServiceProviderTokenManager.cs b/projecttemplates/RelyingPartyLogic/OAuthServiceProviderTokenManager.cs deleted file mode 100644 index 4ae50ce..0000000 --- a/projecttemplates/RelyingPartyLogic/OAuthServiceProviderTokenManager.cs +++ /dev/null @@ -1,112 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthServiceProviderTokenManager.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace RelyingPartyLogic { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Web; - using DotNetOpenAuth.OAuth.ChannelElements; - - public class OAuthServiceProviderTokenManager : OAuthTokenManager, IServiceProviderTokenManager { - /// <summary> - /// Initializes a new instance of the <see cref="OAuthServiceProviderTokenManager"/> class. - /// </summary> - public OAuthServiceProviderTokenManager() { - } - - #region IServiceProviderTokenManager Members - - /// <summary> - /// Gets the Consumer description for a given a Consumer Key. - /// </summary> - /// <param name="consumerKey">The Consumer Key.</param> - /// <returns> - /// A description of the consumer. Never null. - /// </returns> - /// <exception cref="KeyNotFoundException">Thrown if the consumer key cannot be found.</exception> - public IConsumerDescription GetConsumer(string consumerKey) { - try { - return Database.DataContext.Consumers.First(c => c.ConsumerKey == consumerKey); - } catch (InvalidOperationException) { - throw new KeyNotFoundException(); - } - } - - /// <summary> - /// Checks whether a given request token has already been authorized - /// by some user for use by the Consumer that requested it. - /// </summary> - /// <param name="requestToken">The Consumer's request token.</param> - /// <returns> - /// True if the request token has already been fully authorized by the user - /// who owns the relevant protected resources. False if the token has not yet - /// been authorized, has expired or does not exist. - /// </returns> - public bool IsRequestTokenAuthorized(string requestToken) { - return Database.DataContext.IssuedTokens.OfType<IssuedRequestToken>().Any( - t => t.Token == requestToken && t.User != null); - } - - /// <summary> - /// Gets details on the named request token. - /// </summary> - /// <param name="token">The request token.</param> - /// <returns>A description of the token. Never null.</returns> - /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception> - /// <remarks> - /// It is acceptable for implementations to find the token, see that it has expired, - /// delete it from the database and then throw <see cref="KeyNotFoundException"/>, - /// or alternatively it can return the expired token anyway and the OAuth channel will - /// log and throw the appropriate error. - /// </remarks> - public IServiceProviderRequestToken GetRequestToken(string token) { - try { - return Database.DataContext.IssuedTokens.OfType<IssuedRequestToken>().First(tok => tok.Token == token); - } catch (InvalidOperationException) { - throw new KeyNotFoundException(); - } - } - - /// <summary> - /// Gets details on the named access token. - /// </summary> - /// <param name="token">The access token.</param> - /// <returns>A description of the token. Never null.</returns> - /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception> - /// <remarks> - /// It is acceptable for implementations to find the token, see that it has expired, - /// delete it from the database and then throw <see cref="KeyNotFoundException"/>, - /// or alternatively it can return the expired token anyway and the OAuth channel will - /// log and throw the appropriate error. - /// </remarks> - public IServiceProviderAccessToken GetAccessToken(string token) { - try { - return Database.DataContext.IssuedTokens.OfType<IssuedAccessToken>().First(tok => tok.Token == token); - } catch (InvalidOperationException) { - throw new KeyNotFoundException(); - } - } - - /// <summary> - /// Persists any changes made to the token. - /// </summary> - /// <param name="token">The token whose properties have been changed.</param> - /// <remarks> - /// This library will invoke this method after making a set - /// of changes to the token as part of a web request to give the host - /// the opportunity to persist those changes to a database. - /// Depending on the object persistence framework the host site uses, - /// this method MAY not need to do anything (if changes made to the token - /// will automatically be saved without any extra handling). - /// </remarks> - public void UpdateToken(IServiceProviderRequestToken token) { - Database.DataContext.SaveChanges(); - } - - #endregion - } -} diff --git a/projecttemplates/RelyingPartyLogic/OAuthTokenManager.cs b/projecttemplates/RelyingPartyLogic/OAuthTokenManager.cs deleted file mode 100644 index fbf808c..0000000 --- a/projecttemplates/RelyingPartyLogic/OAuthTokenManager.cs +++ /dev/null @@ -1,141 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthTokenManager.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace RelyingPartyLogic { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Security.Cryptography.X509Certificates; - using System.Web; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; - - /// <summary> - /// The token manager this web site uses in its roles both as - /// a consumer and as a service provider. - /// </summary> - public class OAuthTokenManager : ITokenManager { - /// <summary> - /// Initializes a new instance of the <see cref="OAuthTokenManager"/> class. - /// </summary> - protected OAuthTokenManager() { - } - - #region ITokenManager Members - - /// <summary> - /// Gets the Token Secret given a request or access token. - /// </summary> - /// <param name="token">The request or access token.</param> - /// <returns> - /// The secret associated with the given token. - /// </returns> - /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception> - public string GetTokenSecret(string token) { - try { - return Database.DataContext.IssuedTokens.First(t => t.Token == token).TokenSecret; - } catch (InvalidOperationException) { - throw new ArgumentOutOfRangeException(); - } - } - - /// <summary> - /// Stores a newly generated unauthorized request token, secret, and optional - /// application-specific parameters for later recall. - /// </summary> - /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param> - /// <param name="response">The response message that includes the unauthorized request token.</param> - /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception> - /// <remarks> - /// Request tokens stored by this method SHOULD NOT associate any user account with this token. - /// It usually opens up security holes in your application to do so. Instead, you associate a user - /// account with access tokens (not request tokens) in the <see cref="ExpireRequestTokenAndStoreNewAccessToken"/> - /// method. - /// </remarks> - public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) { - Consumer consumer; - try { - consumer = Database.DataContext.Consumers.First(c => c.ConsumerKey == request.ConsumerKey); - } catch (InvalidOperationException) { - throw new ArgumentOutOfRangeException(); - } - - var token = new IssuedRequestToken { - Callback = request.Callback, - Consumer = consumer, - Token = response.Token, - TokenSecret = response.TokenSecret, - }; - string scope; - if (request.ExtraData.TryGetValue("scope", out scope)) { - token.Scope = scope; - } - Database.DataContext.AddToIssuedTokens(token); - Database.DataContext.SaveChanges(); - } - - /// <summary> - /// Deletes a request token and its associated secret and stores a new access token and secret. - /// </summary> - /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param> - /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param> - /// <param name="accessToken">The new access token that is being issued to the Consumer.</param> - /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param> - /// <remarks> - /// <para> - /// Any scope of granted privileges associated with the request token from the - /// original call to <see cref="StoreNewRequestToken"/> should be carried over - /// to the new Access Token. - /// </para> - /// <para> - /// To associate a user account with the new access token, - /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be - /// useful in an ASP.NET web application within the implementation of this method. - /// Alternatively you may store the access token here without associating with a user account, - /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or - /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access - /// token to associate the access token with a user account at that point. - /// </para> - /// </remarks> - public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) { - var requestTokenEntity = Database.DataContext.IssuedTokens.OfType<IssuedRequestToken>() - .Include("User") - .First(t => t.Consumer.ConsumerKey == consumerKey && t.Token == requestToken); - - var accessTokenEntity = new IssuedAccessToken { - Token = accessToken, - TokenSecret = accessTokenSecret, - ExpirationDateUtc = null, // currently, our access tokens don't expire - User = requestTokenEntity.User, - Scope = requestTokenEntity.Scope, - Consumer = requestTokenEntity.Consumer, - }; - - Database.DataContext.DeleteObject(requestTokenEntity); - Database.DataContext.AddToIssuedTokens(accessTokenEntity); - Database.DataContext.SaveChanges(); - } - - /// <summary> - /// Classifies a token as a request token or an access token. - /// </summary> - /// <param name="token">The token to classify.</param> - /// <returns> - /// Request or Access token, or invalid if the token is not recognized. - /// </returns> - public TokenType GetTokenType(string token) { - IssuedToken tok = Database.DataContext.IssuedTokens.FirstOrDefault(t => t.Token == token); - if (tok == null) { - return TokenType.InvalidToken; - } else { - return tok is IssuedAccessToken ? TokenType.AccessToken : TokenType.RequestToken; - } - } - - #endregion - } -} diff --git a/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj b/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj index 338622c..8f11aef 100644 --- a/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj +++ b/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj @@ -121,13 +121,11 @@ <Compile Include="Model.OpenIdAssociation.cs" /> <Compile Include="Model.User.cs" /> <Compile Include="NonceDbStore.cs" /> + <Compile Include="OAuthAuthorizationServer.cs" /> <Compile Include="OAuthAuthenticationModule.cs" /> <Compile Include="OAuthAuthorizationManager.cs" /> - <Compile Include="OAuthConsumerTokenManager.cs" /> <Compile Include="OAuthPrincipalAuthorizationPolicy.cs" /> <Compile Include="OAuthServiceProvider.cs" /> - <Compile Include="OAuthServiceProviderTokenManager.cs" /> - <Compile Include="OAuthTokenManager.cs" /> <Compile Include="Policies.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="RelyingPartyApplicationDbStore.cs" /> @@ -144,12 +142,12 @@ <Project>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</Project> <Name>DotNetOpenAuth</Name> </ProjectReference> - <ProjectReference Include="..\RelyingPartyDatabase\RelyingPartyDatabase.dbproj"> - <Name>RelyingPartyDatabase</Name> - <!-- Deploy the latest SQL script first, so that this project can embed the latest version. --> - <Targets>Build;Deploy</Targets> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> + <ProjectReference Include="..\RelyingPartyDatabase\RelyingPartyDatabase.dbproj"> + <Name>RelyingPartyDatabase</Name> + <!-- Deploy the latest SQL script first, so that this project can embed the latest version. --> + <Targets>Build;Deploy</Targets> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> </ItemGroup> <ItemGroup> <EmbeddedResource Include="CreateDatabase.sql" /> diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx index 7886157..7e07323 100644 --- a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx @@ -29,19 +29,6 @@ <b>Javascript appears to be disabled in your browser. </b>This page requires Javascript to be enabled to better protect your security. </div> - <asp:Panel runat="server" BackColor="Red" ForeColor="White" Font-Bold="true" Visible="false" - ID="OAuth10ConsumerWarning"> - This website is registered with - <asp:Label runat="server" ID="serviceProviderDomainNameLabel" /> - to make authorization requests, but has not been configured to send requests securely. - If you grant access but you did not initiate this request at - <asp:Label runat="server" ID="consumerDomainNameLabel1" />, it may be possible for - other users of - <asp:Label runat="server" ID="consumerDomainNameLabel2" /> - to access your data. We recommend you deny access unless you are certain that you - initiated this request directly with - <asp:Label runat="server" ID="consumerDomainNameLabel3" />. - </asp:Panel> <script language="javascript" type="text/javascript"> //<![CDATA[ diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs index 16e48f0..cd523dd 100644 --- a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs @@ -13,22 +13,23 @@ namespace WebFormsRelyingParty.Members { using System.Web.UI.WebControls; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.OAuth2.Messages; + using RelyingPartyLogic; public partial class OAuthAuthorize : System.Web.UI.Page { + private EndUserAuthorizationRequest pendingRequest; + protected void Page_Load(object sender, EventArgs e) { - if (!IsPostBack) { - var pendingRequest = OAuthServiceProvider.PendingAuthorizationRequest; - if (pendingRequest == null) { - Response.Redirect("AccountInfo.aspx"); - } + this.pendingRequest = OAuthServiceProvider.AuthorizationServer.ReadAuthorizationRequest(); + if (this.pendingRequest == null) { + Response.Redirect("AccountInfo.aspx"); + } + if (!IsPostBack) { this.csrfCheck.Value = Code.SiteUtilities.SetCsrfCookie(); - this.consumerNameLabel.Text = HttpUtility.HtmlEncode(OAuthServiceProvider.PendingAuthorizationConsumer.Name); - this.OAuth10ConsumerWarning.Visible = pendingRequest.IsUnsafeRequest; - - this.serviceProviderDomainNameLabel.Text = HttpUtility.HtmlEncode(this.Request.Url.Host); - this.consumerDomainNameLabel3.Text = this.consumerDomainNameLabel2.Text = this.consumerDomainNameLabel1.Text = HttpUtility.HtmlEncode(OAuthServiceProvider.PendingAuthorizationConsumer.Name); + var requestingClient = Database.DataContext.Consumers.First(c => c.ConsumerKey == this.pendingRequest.ClientIdentifier); + this.consumerNameLabel.Text = HttpUtility.HtmlEncode(requestingClient.Name); } else { Code.SiteUtilities.VerifyCsrfCookie(this.csrfCheck.Value); } @@ -36,31 +37,12 @@ namespace WebFormsRelyingParty.Members { protected void yesButton_Click(object sender, EventArgs e) { this.outerMultiView.SetActiveView(this.authorizationGrantedView); - - var consumer = OAuthServiceProvider.PendingAuthorizationConsumer; - var tokenManager = OAuthServiceProvider.ServiceProvider.TokenManager; - var pendingRequest = OAuthServiceProvider.PendingAuthorizationRequest; - ITokenContainingMessage requestTokenMessage = pendingRequest; - var requestToken = tokenManager.GetRequestToken(requestTokenMessage.Token); - - OAuthServiceProvider.AuthorizePendingRequestToken(); - - // The rest of this method only executes if we couldn't automatically - // redirect to the consumer. - if (pendingRequest.IsUnsafeRequest) { - this.verifierMultiView.SetActiveView(this.noCallbackView); - } else { - this.verifierMultiView.SetActiveView(this.verificationCodeView); - string verifier = ServiceProvider.CreateVerificationCode(consumer.VerificationCodeFormat, consumer.VerificationCodeLength); - this.verificationCodeLabel.Text = HttpUtility.HtmlEncode(verifier); - requestToken.VerificationCode = verifier; - tokenManager.UpdateToken(requestToken); - } + OAuthServiceProvider.AuthorizationServer.ApproveAuthorizationRequest(this.pendingRequest, HttpContext.Current.User.Identity.Name); } protected void noButton_Click(object sender, EventArgs e) { this.outerMultiView.SetActiveView(this.authorizationDeniedView); - OAuthServiceProvider.PendingAuthorizationRequest = null; + OAuthServiceProvider.AuthorizationServer.RejectAuthorizationRequest(this.pendingRequest); } } } diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs index 20d5ea9..19947de 100644 --- a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4927 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ @@ -68,51 +67,6 @@ namespace WebFormsRelyingParty.Members { protected global::System.Web.UI.WebControls.HiddenField csrfCheck; /// <summary> - /// OAuth10ConsumerWarning control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Panel OAuth10ConsumerWarning; - - /// <summary> - /// serviceProviderDomainNameLabel control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Label serviceProviderDomainNameLabel; - - /// <summary> - /// consumerDomainNameLabel1 control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Label consumerDomainNameLabel1; - - /// <summary> - /// consumerDomainNameLabel2 control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Label consumerDomainNameLabel2; - - /// <summary> - /// consumerDomainNameLabel3 control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.Label consumerDomainNameLabel3; - - /// <summary> /// authorizationGrantedView control. /// </summary> /// <remarks> diff --git a/projecttemplates/WebFormsRelyingParty/OAuth.ashx b/projecttemplates/WebFormsRelyingParty/OAuth.ashx deleted file mode 100644 index 6176757..0000000 --- a/projecttemplates/WebFormsRelyingParty/OAuth.ashx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebHandler Language="C#" CodeBehind="OAuth.ashx.cs" Class="WebFormsRelyingParty.OAuth" %> diff --git a/projecttemplates/WebFormsRelyingParty/OAuth.ashx.cs b/projecttemplates/WebFormsRelyingParty/OAuth.ashx.cs deleted file mode 100644 index cb7c819..0000000 --- a/projecttemplates/WebFormsRelyingParty/OAuth.ashx.cs +++ /dev/null @@ -1,66 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuth.ashx.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace WebFormsRelyingParty { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Web; - using System.Web.SessionState; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.Messages; - using RelyingPartyLogic; - - /// <summary> - /// Responds to incoming OAuth Service Provider messages. - /// </summary> - public class OAuth : IHttpHandler, IRequiresSessionState { - /// <summary> - /// Initializes a new instance of the <see cref="OAuth"/> class. - /// </summary> - public OAuth() { - } - - /// <summary> - /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance. - /// </summary> - /// <returns> - /// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. - /// </returns> - public bool IsReusable { - get { return true; } - } - - /// <summary> - /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. - /// </summary> - /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> - public void ProcessRequest(HttpContext context) { - var serviceProvider = OAuthServiceProvider.ServiceProvider; - var requestMessage = serviceProvider.ReadRequest(new HttpRequestInfo(context.Request)); - - UnauthorizedTokenRequest unauthorizedTokenRequestMessage; - AuthorizedTokenRequest authorizedTokenRequestMessage; - UserAuthorizationRequest userAuthorizationRequest; - if ((unauthorizedTokenRequestMessage = requestMessage as UnauthorizedTokenRequest) != null) { - var response = serviceProvider.PrepareUnauthorizedTokenMessage(unauthorizedTokenRequestMessage); - serviceProvider.Channel.Send(response); - } else if ((authorizedTokenRequestMessage = requestMessage as AuthorizedTokenRequest) != null) { - var response = serviceProvider.PrepareAccessTokenMessage(authorizedTokenRequestMessage); - serviceProvider.Channel.Send(response); - } else if ((userAuthorizationRequest = requestMessage as UserAuthorizationRequest) != null) { - // This is a browser opening to allow the user to authorize a request token, - // so redirect to the authorization page, which will automatically redirect - // to have the user log in if necessary. - OAuthServiceProvider.PendingAuthorizationRequest = userAuthorizationRequest; - HttpContext.Current.Response.Redirect("~/Members/OAuthAuthorize.aspx"); - } else { - throw new InvalidOperationException(); - } - } - } -} diff --git a/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx new file mode 100644 index 0000000..3d1cd86 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="OAuthTokenEndpoint.ashx.cs" Class="WebFormsRelyingParty.OAuthTokenEndpoint" %> diff --git a/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs new file mode 100644 index 0000000..ca9b399 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/OAuthTokenEndpoint.ashx.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthTokenEndpoint.ashx.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using System.Web.SessionState; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2; + using RelyingPartyLogic; + + /// <summary> + /// An OAuth 2.0 token endpoint. + /// </summary> + public class OAuthTokenEndpoint : IHttpHandler, IRequiresSessionState { + /// <summary> + /// Initializes a new instance of the <see cref="OAuthTokenEndpoint"/> class. + /// </summary> + public OAuthTokenEndpoint() { + } + + /// <summary> + /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance. + /// </summary> + /// <returns> + /// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. + /// </returns> + public bool IsReusable { + get { return true; } + } + + /// <summary> + /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. + /// </summary> + /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> + public void ProcessRequest(HttpContext context) { + var serviceProvider = OAuthServiceProvider.AuthorizationServer; + IDirectResponseProtocolMessage response; + if (serviceProvider.TryPrepareAccessTokenResponse(new HttpRequestInfo(context.Request), out response)) { + serviceProvider.Channel.Send(response); + } else { + throw new InvalidOperationException(); + } + } + } +} diff --git a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj index 8faeef5..eb81910 100644 --- a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj +++ b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj @@ -153,8 +153,8 @@ <Compile Include="Members\Default.aspx.designer.cs"> <DependentUpon>Default.aspx</DependentUpon> </Compile> - <Compile Include="OAuth.ashx.cs"> - <DependentUpon>OAuth.ashx</DependentUpon> + <Compile Include="OAuthTokenEndpoint.ashx.cs"> + <DependentUpon>OAuthTokenEndpoint.ashx</DependentUpon> </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Setup.aspx.cs"> @@ -235,7 +235,7 @@ <ItemGroup> <Content Include="images\verisign.gif" /> <Content Include="Members\OAuthAuthorize.aspx" /> - <Content Include="OAuth.ashx" /> + <Content Include="OAuthTokenEndpoint.ashx" /> <Content Include="PrivacyPolicy.aspx" /> </ItemGroup> <ItemGroup> diff --git a/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs index c99fbc3..83b5191 100644 --- a/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OAuth2 { [ContractClass(typeof(IAuthorizationServerContract))] public interface IAuthorizationServer { /// <summary> - /// Gets the secret used to symmetrically encrypt and sign verification codes and refresh tokens. + /// Gets the secret used to symmetrically encrypt and sign authorization codes and refresh tokens. /// </summary> /// <remarks> /// This secret should be kept strictly confidential in the authorization server(s) @@ -39,9 +39,9 @@ namespace DotNetOpenAuth.OAuth2 { RSAParameters AccessTokenSigningPrivateKey { get; } /// <summary> - /// Gets the verification code nonce store to use to ensure that verification codes can only be used once. + /// Gets the authorization code nonce store to use to ensure that authorization codes can only be used once. /// </summary> - /// <value>The verification code nonce store.</value> + /// <value>The authorization code nonce store.</value> INonceStore VerificationCodeNonceStore { get; } /// <summary> @@ -89,7 +89,7 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Gets the secret used to symmetrically encrypt and sign verification codes and refresh tokens. + /// Gets the secret used to symmetrically encrypt and sign authorization codes and refresh tokens. /// </summary> /// <value></value> /// <remarks> @@ -117,9 +117,9 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Gets the verification code nonce store to use to ensure that verification codes can only be used once. + /// Gets the authorization code nonce store to use to ensure that authorization codes can only be used once. /// </summary> - /// <value>The verification code nonce store.</value> + /// <value>The authorization code nonce store.</value> INonceStore IAuthorizationServer.VerificationCodeNonceStore { get { Contract.Ensures(Contract.Result<INonceStore>() != null); diff --git a/src/DotNetOpenAuth/OAuth2/IConsumerDescription.cs b/src/DotNetOpenAuth/OAuth2/IConsumerDescription.cs index f0fc20a..bfb3ecc 100644 --- a/src/DotNetOpenAuth/OAuth2/IConsumerDescription.cs +++ b/src/DotNetOpenAuth/OAuth2/IConsumerDescription.cs @@ -9,7 +9,7 @@ namespace DotNetOpenAuth.OAuth2 { using System.Security.Cryptography.X509Certificates; /// <summary> - /// A description of a consumer from a Service Provider's point of view. + /// A description of a client from an Authorization Server's point of view. /// </summary> public interface IConsumerDescription { /// <summary> @@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OAuth2 { string Secret { get; } /// <summary> - /// Gets the callback URI that this consumer has pre-registered with the service provider, if any. + /// Gets the callback URI that this client has pre-registered with the service provider, if any. /// </summary> /// <value>A URI that user authorization responses should be directed to; or <c>null</c> if no preregistered callback was arranged.</value> Uri Callback { get; } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs index d66c2d8..62cad53 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs @@ -15,7 +15,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// to indicate that user authorization was granted, and to return the user /// to the Client where they started their experience. /// </summary> - internal abstract class EndUserAuthorizationSuccessResponseBase : MessageBase, IMessageWithClientState { + public abstract class EndUserAuthorizationSuccessResponseBase : MessageBase, IMessageWithClientState { /// <summary> /// Initializes a new instance of the <see cref="EndUserAuthorizationSuccessResponseBase"/> class. /// </summary> diff --git a/src/DotNetOpenAuth/OAuth2/ResourceServer.cs b/src/DotNetOpenAuth/OAuth2/ResourceServer.cs index 534f741..3a86d29 100644 --- a/src/DotNetOpenAuth/OAuth2/ResourceServer.cs +++ b/src/DotNetOpenAuth/OAuth2/ResourceServer.cs @@ -10,6 +10,8 @@ namespace DotNetOpenAuth.OAuth2 { using System.Diagnostics.Contracts; using System.Linq; using System.Net; + using System.Security.Principal; + using System.ServiceModel.Channels; using System.Text; using System.Text.RegularExpressions; using System.Web; @@ -97,5 +99,41 @@ namespace DotNetOpenAuth.OAuth2 { return this.Channel.PrepareResponse(response); } } + + /// <summary> + /// Discovers what access the client should have considering the access token in the current request. + /// </summary> + /// <param name="httpRequestInfo">The HTTP request info.</param> + /// <param name="principal">The principal that contains the user and roles that the access token is authorized for.</param> + /// <returns> + /// An error to return to the client if access is not authorized; <c>null</c> if access is granted. + /// </returns> + public virtual OutgoingWebResponse VerifyAccess(HttpRequestInfo httpRequestInfo, out IPrincipal principal) { + string username, scope; + var result = this.VerifyAccess(httpRequestInfo, out username, out scope); + if (result == null) { + principal = new OAuth.ChannelElements.OAuthPrincipal(username, scope != null ? scope.Split(' ') : new string[0]); + } else { + principal = null; + } + + return result; + } + + /// <summary> + /// Discovers what access the client should have considering the access token in the current request. + /// </summary> + /// <param name="request">HTTP details from an incoming WCF message.</param> + /// <param name="requestUri">The URI of the WCF service endpoint.</param> + /// <param name="principal">The principal that contains the user and roles that the access token is authorized for.</param> + /// <returns> + /// An error to return to the client if access is not authorized; <c>null</c> if access is granted. + /// </returns> + public virtual OutgoingWebResponse VerifyAccess(HttpRequestMessageProperty request, Uri requestUri, out IPrincipal principal) { + Contract.Requires<ArgumentNullException>(request != null, "request"); + Contract.Requires<ArgumentNullException>(requestUri != null, "requestUri"); + + return this.VerifyAccess(new HttpRequestInfo(request, requestUri), out principal); + } } } diff --git a/src/DotNetOpenAuth/OAuth2/WebServerAuthorizationServer.cs b/src/DotNetOpenAuth/OAuth2/WebServerAuthorizationServer.cs index 67ea1d6..66bc96d 100644 --- a/src/DotNetOpenAuth/OAuth2/WebServerAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuth2/WebServerAuthorizationServer.cs @@ -48,8 +48,7 @@ namespace DotNetOpenAuth.OAuth2 { public void ApproveAuthorizationRequest(EndUserAuthorizationRequest authorizationRequest, string username, Uri callback = null) { Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); - var response = this.PrepareApproveAuthorizationRequest(authorizationRequest, callback); - response.AuthorizingUsername = username; + var response = this.PrepareApproveAuthorizationRequest(authorizationRequest, username, callback); this.Channel.Send(response); } @@ -101,8 +100,9 @@ namespace DotNetOpenAuth.OAuth2 { return response; } - internal EndUserAuthorizationSuccessResponseBase PrepareApproveAuthorizationRequest(EndUserAuthorizationRequest authorizationRequest, Uri callback = null) { + public EndUserAuthorizationSuccessResponseBase PrepareApproveAuthorizationRequest(EndUserAuthorizationRequest authorizationRequest, string username, Uri callback = null) { Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(username)); Contract.Ensures(Contract.Result<EndUserAuthorizationSuccessResponseBase>() != null); if (callback == null) { @@ -111,8 +111,7 @@ namespace DotNetOpenAuth.OAuth2 { var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); EndUserAuthorizationSuccessResponseBase response; - switch (authorizationRequest.ResponseType) - { + switch (authorizationRequest.ResponseType) { case EndUserAuthorizationResponseType.AccessToken: response = new EndUserAuthorizationSuccessAccessTokenResponse(callback, authorizationRequest); break; @@ -123,7 +122,8 @@ namespace DotNetOpenAuth.OAuth2 { default: throw ErrorUtilities.ThrowInternal("Unexpected response type."); } - + + response.AuthorizingUsername = username; return response; } |