1- namespace OAuth2ProtectedWebApi {
1+ namespace OAuth2ProtectedWebApi . Code {
22 using System ;
33 using System . Collections . Generic ;
44 using System . Linq ;
55 using System . Web ;
6+ using System . Web . Security ;
67 using DotNetOpenAuth . Messaging . Bindings ;
78 using DotNetOpenAuth . OAuth2 ;
89 using DotNetOpenAuth . OAuth2 . ChannelElements ;
910 using DotNetOpenAuth . OAuth2 . Messages ;
1011 using OAuth2ProtectedWebApi . Code ;
1112
13+ /// <summary>
14+ /// Provides application-specific policy and persistence for OAuth 2.0 authorization servers.
15+ /// </summary>
1216 public class AuthorizationServerHost : IAuthorizationServerHost {
13- private static ICryptoKeyStore cryptoKeyStore = MemoryCryptoKeyStore . Instance ;
17+ /// <summary>
18+ /// Storage for the cryptographic keys used to protect authorization codes, refresh tokens and access tokens.
19+ /// </summary>
20+ /// <remarks>
21+ /// A single, hard-coded symmetric key is hardly adequate. Applications that rely on decent security should
22+ /// replace this implementation with one that actually stores and retrieves keys in some persistent store
23+ /// (e.g. a database). DotNetOpenAuth will automatically take care of generating, rotating, and expiring keys
24+ /// if you provide a real implementation of this interface.
25+ /// TODO: Consider replacing use of <see cref="HardCodedKeyCryptoKeyStore"/> with a real persisted database table.
26+ /// </remarks>
27+ internal static readonly ICryptoKeyStore HardCodedCryptoKeyStore = new HardCodedKeyCryptoKeyStore ( "p7J1L24Qj4KGYUOrnfENF0XAhqn6rZc5dx4nxvI22Kg=" ) ;
1428
29+ /// <summary>
30+ /// Gets the store for storing crypto keys used to symmetrically encrypt and sign authorization codes and refresh tokens.
31+ /// </summary>
32+ /// <remarks>
33+ /// This store should be kept strictly confidential in the authorization server(s)
34+ /// and NOT shared with the resource server. Anyone with these secrets can mint
35+ /// tokens to essentially grant themselves access to anything they want.
36+ /// </remarks>
1537 public ICryptoKeyStore CryptoKeyStore {
16- get { return cryptoKeyStore ; }
38+ get { return HardCodedCryptoKeyStore ; }
1739 }
1840
41+ /// <summary>
42+ /// Gets the authorization code nonce store to use to ensure that authorization codes can only be used once.
43+ /// </summary>
44+ /// <value>
45+ /// The authorization code nonce store.
46+ /// </value>
1947 public INonceStore NonceStore {
2048 get {
21- // Implementing a nonce store is a good idea as it mitigates replay attacks .
49+ // TODO: Consider implementing a nonce store to mitigate replay attacks on authorization codes .
2250 return null ;
2351 }
2452 }
2553
54+ /// <summary>
55+ /// Acquires the access token and related parameters that go into the formulation of the token endpoint's response to a client.
56+ /// </summary>
57+ /// <param name="accessTokenRequestMessage">Details regarding the resources that the access token will grant access to, and the identity of the client
58+ /// that will receive that access.
59+ /// Based on this information the receiving resource server can be determined and the lifetime of the access
60+ /// token can be set based on the sensitivity of the resources.</param>
61+ /// <returns>
62+ /// A non-null parameters instance that DotNetOpenAuth will dispose after it has been used.
63+ /// </returns>
2664 public AccessTokenResult CreateAccessToken ( IAccessTokenRequest accessTokenRequestMessage ) {
27- var accessToken = new AuthorizationServerAccessToken ( ) ;
28- accessToken . Lifetime = TimeSpan . FromHours ( 1 ) ;
29- accessToken . SymmetricKeyStore = this . CryptoKeyStore ;
65+ // If your resource server and authorization server are different web apps,
66+ // consider using asymmetric keys instead of symmetric ones by setting different
67+ // properties on the access token below.
68+ var accessToken = new AuthorizationServerAccessToken {
69+ Lifetime = TimeSpan . FromHours ( 1 ) ,
70+ SymmetricKeyStore = this . CryptoKeyStore ,
71+ } ;
3072 var result = new AccessTokenResult ( accessToken ) ;
3173 return result ;
3274 }
3375
76+ /// <summary>
77+ /// Gets the client with a given identifier.
78+ /// </summary>
79+ /// <param name="clientIdentifier">The client identifier.</param>
80+ /// <returns>
81+ /// The client registration. Never null.
82+ /// </returns>
83+ /// <exception cref="ArgumentException">Thrown when no client with the given identifier is registered with this authorization server.</exception>
3484 public IClientDescription GetClient ( string clientIdentifier ) {
35- return new ClientDescription ( "b" , new Uri ( "http://www.microsoft.com/en-us/default.aspx" ) , ClientType . Confidential ) ;
85+ // TODO: Consider adding a clients table in your database to track actual client accounts
86+ // with authenticating secrets.
87+ // For now, just allow all clients regardless of ID, and consider them "Public" clients.
88+ return new ClientDescription ( ) ;
3689 }
3790
91+ /// <summary>
92+ /// Determines whether a described authorization is (still) valid.
93+ /// </summary>
94+ /// <param name="authorization">The authorization.</param>
95+ /// <returns>
96+ /// <c>true</c> if the original authorization is still valid; otherwise, <c>false</c>.
97+ /// </returns>
98+ /// <remarks>
99+ /// <para>When establishing that an authorization is still valid,
100+ /// it's very important to only match on recorded authorizations that
101+ /// meet these criteria:</para>
102+ /// 1) The client identifier matches.
103+ /// 2) The user account matches.
104+ /// 3) The scope on the recorded authorization must include all scopes in the given authorization.
105+ /// 4) The date the recorded authorization was issued must be <em>no later</em> that the date the given authorization was issued.
106+ /// <para>One possible scenario is where the user authorized a client, later revoked authorization,
107+ /// and even later reinstated authorization. This subsequent recorded authorization
108+ /// would not satisfy requirement #4 in the above list. This is important because the revocation
109+ /// the user went through should invalidate all previously issued tokens as a matter of
110+ /// security in the event the user was revoking access in order to sever authorization on a stolen
111+ /// account or piece of hardware in which the tokens were stored. </para>
112+ /// </remarks>
38113 public bool IsAuthorizationValid ( IAuthorizationDescription authorization ) {
114+ // If your application supports access revocation (highly recommended),
115+ // this method should return false if the specified authorization is not
116+ // discovered in your current authorizations table.
117+ //// TODO: code here
118+
39119 return true ;
40120 }
41121
122+ /// <summary>
123+ /// Determines whether a given set of resource owner credentials is valid based on the authorization server's user database
124+ /// and if so records an authorization entry such that subsequent calls to <see cref="IsAuthorizationValid" /> would
125+ /// return <c>true</c>.
126+ /// </summary>
127+ /// <param name="userName">Username on the account.</param>
128+ /// <param name="password">The user's password.</param>
129+ /// <param name="accessRequest">The access request the credentials came with.
130+ /// This may be useful if the authorization server wishes to apply some policy based on the client that is making the request.</param>
131+ /// <returns>
132+ /// A value that describes the result of the authorization check.
133+ /// </returns>
134+ /// <exception cref="System.NotSupportedException"></exception>
42135 public AutomatedUserAuthorizationCheckResponse CheckAuthorizeResourceOwnerCredentialGrant ( string userName , string password , IAccessTokenRequest accessRequest ) {
43- throw new NotSupportedException ( ) ;
136+ // TODO: Consider only accepting resource owner credential grants from specific clients
137+ // based on accessRequest.ClientIdentifier and accessRequest.ClientAuthenticated.
138+ if ( Membership . ValidateUser ( userName , password ) ) {
139+ // Add an entry to your authorization table to record that access was granted so that
140+ // you can conditionally return true from IsAuthorizationValid when the row is discovered.
141+ //// TODO: code here
142+
143+ // Inform DotNetOpenAuth that it may proceed to issue an access token.
144+ return new AutomatedUserAuthorizationCheckResponse ( accessRequest , true , Membership . GetUser ( userName ) . UserName ) ;
145+ } else {
146+ return new AutomatedUserAuthorizationCheckResponse ( accessRequest , false , null ) ;
147+ }
44148 }
45149
150+ /// <summary>
151+ /// Determines whether an access token request given a client credential grant should be authorized
152+ /// and if so records an authorization entry such that subsequent calls to <see cref="IsAuthorizationValid" /> would
153+ /// return <c>true</c>.
154+ /// </summary>
155+ /// <param name="accessRequest">The access request the credentials came with.
156+ /// This may be useful if the authorization server wishes to apply some policy based on the client that is making the request.</param>
157+ /// <returns>
158+ /// A value that describes the result of the authorization check.
159+ /// </returns>
160+ /// <exception cref="System.NotSupportedException"></exception>
46161 public AutomatedAuthorizationCheckResponse CheckAuthorizeClientCredentialsGrant ( IAccessTokenRequest accessRequest ) {
47- throw new NotSupportedException ( ) ;
162+ // TODO: Consider implementing this if your application should support clients that access data that
163+ // doesn't belong to specific people, or clients that have elevated privileges and can access other
164+ // people's data.
165+ if ( accessRequest . ClientAuthenticated ) {
166+ // Before returning a positive response, be *very careful* to validate the requested access scope
167+ // to make sure it is appropriate for the requesting client.
168+ throw new NotSupportedException ( ) ;
169+ } else {
170+ // Only authenticated clients should be given access.
171+ return new AutomatedAuthorizationCheckResponse ( accessRequest , false ) ;
172+ }
48173 }
49174 }
50175}
0 commit comments