diff options
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs')
-rw-r--r-- | src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs | 63 |
1 files changed, 44 insertions, 19 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs index 2830ab8..9540d10 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs @@ -34,6 +34,8 @@ namespace DotNetOpenAuth.OAuth2 { this.AccessTokenAnalyzer = accessTokenAnalyzer; this.Channel = new OAuth2ResourceServerChannel(); + this.ResourceOwnerPrincipalPrefix = string.Empty; + this.ClientPrincipalPrefix = "client:"; } /// <summary> @@ -43,6 +45,18 @@ namespace DotNetOpenAuth.OAuth2 { public IAccessTokenAnalyzer AccessTokenAnalyzer { get; private set; } /// <summary> + /// Gets or sets the prefix to apply to a resource owner's username when used as the username in an <see cref="IPrincipal"/>. + /// </summary> + /// <value>The default value is the empty string.</value> + public string ResourceOwnerPrincipalPrefix { get; set; } + + /// <summary> + /// Gets or sets the prefix to apply to a client identifier when used as the username in an <see cref="IPrincipal"/>. + /// </summary> + /// <value>The default value is "client:"</value> + public string ClientPrincipalPrefix { get; set; } + + /// <summary> /// Gets the channel. /// </summary> /// <value>The channel.</value> @@ -51,50 +65,48 @@ namespace DotNetOpenAuth.OAuth2 { /// <summary> /// Discovers what access the client should have considering the access token in the current request. /// </summary> - /// <param name="userName">The name on the account the client has access to.</param> - /// <param name="scope">The set of operations the client is authorized for.</param> + /// <param name="accessToken">Receives the access token describing the authorization the client has.</param> /// <returns>An error to return to the client if access is not authorized; <c>null</c> if access is granted.</returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Try pattern")] [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] - public OutgoingWebResponse VerifyAccess(out string userName, out HashSet<string> scope) { - return this.VerifyAccess(this.Channel.GetRequestFromContext(), out userName, out scope); + public OutgoingWebResponse VerifyAccess(out AccessToken accessToken) { + return this.VerifyAccess(this.Channel.GetRequestFromContext(), out accessToken); } /// <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="userName">The name on the account the client has access to.</param> - /// <param name="scope">The set of operations the client is authorized for.</param> + /// <param name="accessToken">Receives the access token describing the authorization the client has.</param> /// <returns> /// An error to return to the client if access is not authorized; <c>null</c> if access is granted. /// </returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Try pattern")] - public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out string userName, out HashSet<string> scope) { + public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out AccessToken accessToken) { Requires.NotNull(httpRequestInfo, "httpRequestInfo"); AccessProtectedResourceRequest request = null; try { if (this.Channel.TryReadFromRequest<AccessProtectedResourceRequest>(httpRequestInfo, out request)) { - if (this.AccessTokenAnalyzer.TryValidateAccessToken(request, request.AccessToken, out userName, out scope)) { - // No errors to return. - return null; + accessToken = this.AccessTokenAnalyzer.DeserializeAccessToken(request, request.AccessToken); + ErrorUtilities.VerifyHost(accessToken != null, "IAccessTokenAnalyzer.DeserializeAccessToken returned a null reslut."); + if (string.IsNullOrEmpty(accessToken.User) && string.IsNullOrEmpty(accessToken.ClientIdentifier)) { + Logger.OAuth.Error("Access token rejected because both the username and client id properties were null or empty."); + ErrorUtilities.ThrowProtocol(ResourceServerStrings.InvalidAccessToken); } - throw ErrorUtilities.ThrowProtocol(ResourceServerStrings.InvalidAccessToken); + return null; } else { var response = new UnauthorizedResponse(new ProtocolException(ResourceServerStrings.MissingAccessToken)); - userName = null; - scope = null; + accessToken = null; return this.Channel.PrepareResponse(response); } } catch (ProtocolException ex) { var response = request != null ? new UnauthorizedResponse(request, ex) : new UnauthorizedResponse(ex); - userName = null; - scope = null; + accessToken = null; return this.Channel.PrepareResponse(response); } } @@ -109,10 +121,23 @@ namespace DotNetOpenAuth.OAuth2 { /// </returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out IPrincipal principal) { - string username; - HashSet<string> scope; - var result = this.VerifyAccess(httpRequestInfo, out username, out scope); - principal = result == null ? new OAuthPrincipal(username, scope != null ? scope.ToArray() : new string[0]) : null; + AccessToken accessToken; + var result = this.VerifyAccess(httpRequestInfo, out accessToken); + if (result == null) { + // Mitigates attacks on this approach of differentiating clients from resource owners + // by checking that a username doesn't look suspiciously engineered to appear like the other type. + ErrorUtilities.VerifyProtocol(accessToken.User == null || string.IsNullOrEmpty(this.ClientPrincipalPrefix) || !accessToken.User.StartsWith(this.ClientPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ResourceOwnerNameLooksLikeClientIdentifier); + ErrorUtilities.VerifyProtocol(accessToken.ClientIdentifier == null || string.IsNullOrEmpty(this.ResourceOwnerPrincipalPrefix) || !accessToken.ClientIdentifier.StartsWith(this.ResourceOwnerPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ClientIdentifierLooksLikeResourceOwnerName); + + string principalUserName = !string.IsNullOrEmpty(accessToken.User) + ? this.ResourceOwnerPrincipalPrefix + accessToken.User + : this.ClientPrincipalPrefix + accessToken.ClientIdentifier; + string[] principalScope = accessToken.Scope != null ? accessToken.Scope.ToArray() : new string[0]; + principal = new OAuthPrincipal(principalUserName, principalScope); + } else { + principal = null; + } + return result; } |