summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs')
-rw-r--r--src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs194
1 files changed, 144 insertions, 50 deletions
diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs
index 3f4bb5b..e4a8a48 100644
--- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs
+++ b/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/UnauthorizedResponse.cs
@@ -6,110 +6,204 @@
namespace DotNetOpenAuth.OAuth2.Messages {
using System;
+ using System.Collections.Generic;
using System.Diagnostics.Contracts;
+ using System.Globalization;
using System.Net;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.ChannelElements;
/// <summary>
- /// A direct response that is simply a 401 Unauthorized with an
- /// WWW-Authenticate: OAuth header.
+ /// A direct response sent in response to a rejected Bearer access token.
/// </summary>
- internal class UnauthorizedResponse : MessageBase, IHttpDirectResponse {
+ /// <remarks>
+ /// This satisfies the spec in: http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#authn-header
+ /// </remarks>
+ public class UnauthorizedResponse : MessageBase, IHttpDirectResponse {
/// <summary>
- /// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class.
+ /// The headers in the response message.
/// </summary>
- /// <param name="exception">The exception.</param>
- /// <param name="version">The protocol version.</param>
- internal UnauthorizedResponse(ProtocolException exception, Version version = null)
- : base(version ?? Protocol.Default.Version) {
- Requires.NotNull(exception, "exception");
- this.ErrorMessage = exception.Message;
- }
+ private readonly WebHeaderCollection headers = new WebHeaderCollection();
/// <summary>
/// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class.
/// </summary>
- /// <param name="request">The request.</param>
- internal UnauthorizedResponse(IDirectedProtocolMessage request)
- : base(request) {
- this.Realm = "Service";
+ /// <param name="version">The protocol version.</param>
+ protected UnauthorizedResponse(Version version = null)
+ : base(version ?? Protocol.Default.Version) {
}
/// <summary>
/// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class.
/// </summary>
/// <param name="request">The request.</param>
- /// <param name="exception">The exception.</param>
- internal UnauthorizedResponse(IDirectedProtocolMessage request, ProtocolException exception)
- : this(request) {
- Requires.NotNull(exception, "exception");
- this.ErrorMessage = exception.Message;
+ protected UnauthorizedResponse(IDirectedProtocolMessage request)
+ : base(request) {
}
#region IHttpDirectResponse Members
/// <summary>
- /// Gets the HTTP status code that the direct response should be sent with.
+ /// Gets or sets the HTTP status code that the direct response should be sent with.
/// </summary>
- HttpStatusCode IHttpDirectResponse.HttpStatusCode {
- get { return HttpStatusCode.Unauthorized; }
- }
+ public HttpStatusCode HttpStatusCode { get; set; }
/// <summary>
/// Gets the HTTP headers to add to the response.
/// </summary>
/// <value>May be an empty collection, but must not be <c>null</c>.</value>
- WebHeaderCollection IHttpDirectResponse.Headers {
- get {
- return new WebHeaderCollection() {
- { HttpResponseHeader.WwwAuthenticate, Protocol.BearerHttpAuthorizationScheme },
- };
- }
+ public WebHeaderCollection Headers {
+ get { return this.headers; }
}
#endregion
/// <summary>
- /// Gets or sets the error message.
+ /// Gets or sets the well known error code.
/// </summary>
- /// <value>The error message.</value>
- [MessagePart("error")]
- internal string ErrorMessage { get; set; }
+ /// <value>One of the values from <see cref="Protocol.BearerTokenErrorCodes"/>.</value>
+ [MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.ErrorCode)]
+ public string ErrorCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets a human-readable explanation for developers that is not meant to be displayed to end users.
+ /// </summary>
+ [MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.ErrorDescription)]
+ public string ErrorDescription { get; set; }
+
+ /// <summary>
+ /// Gets or sets an absolute URI identifying a human-readable web page explaining the error.
+ /// </summary>
+ [MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.ErrorUri)]
+ public Uri ErrorUri { get; set; }
/// <summary>
/// Gets or sets the realm.
/// </summary>
/// <value>The realm.</value>
- [MessagePart("realm")]
- internal string Realm { get; set; }
+ [MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.Realm)]
+ public string Realm { get; set; }
/// <summary>
/// Gets or sets the scope.
/// </summary>
/// <value>The scope.</value>
- [MessagePart("scope")]
- internal string Scope { get; set; }
+ [MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.Scope, Encoder = typeof(ScopeEncoder))]
+ public HashSet<string> Scope { get; set; }
+
+ /// <summary>
+ /// Gets the scheme to use in the WWW-Authenticate header.
+ /// </summary>
+ internal virtual string Scheme {
+ get { return Protocol.BearerHttpAuthorizationScheme; }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class
+ /// to inform the client that the request was invalid.
+ /// </summary>
+ /// <param name="exception">The exception.</param>
+ /// <param name="version">The version of OAuth 2 that is in use.</param>
+ /// <returns>The error message.</returns>
+ internal static UnauthorizedResponse InvalidRequest(ProtocolException exception, Version version = null) {
+ Requires.NotNull(exception, "exception");
+ var message = new UnauthorizedResponse(version) {
+ ErrorCode = Protocol.BearerTokenErrorCodes.InvalidRequest,
+ ErrorDescription = exception.Message,
+ HttpStatusCode = System.Net.HttpStatusCode.BadRequest,
+ };
+
+ return message;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class
+ /// to inform the client that the bearer token included in the request was rejected.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="exception">The exception.</param>
+ /// <returns>The error message.</returns>
+ internal static UnauthorizedResponse InvalidToken(IDirectedProtocolMessage request, ProtocolException exception) {
+ Requires.NotNull(request, "request");
+ Requires.NotNull(exception, "exception");
+ var message = new UnauthorizedResponse(request) {
+ ErrorCode = Protocol.BearerTokenErrorCodes.InvalidToken,
+ ErrorDescription = exception.Message,
+ HttpStatusCode = System.Net.HttpStatusCode.Unauthorized,
+ };
+
+ return message;
+ }
/// <summary>
- /// Gets or sets the algorithms.
+ /// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class
+ /// to inform the client of the required set of scopes required to perform this operation.
/// </summary>
- /// <value>The algorithms.</value>
- [MessagePart("algorithms")]
- internal string Algorithms { get; set; }
+ /// <param name="request">The request.</param>
+ /// <param name="requiredScopes">The set of scopes required to perform this operation.</param>
+ /// <returns>The error message.</returns>
+ internal static UnauthorizedResponse InsufficientScope(IDirectedProtocolMessage request, HashSet<string> requiredScopes) {
+ Requires.NotNull(request, "request");
+ Requires.NotNull(requiredScopes, "requiredScopes");
+ var message = new UnauthorizedResponse(request) {
+ HttpStatusCode = System.Net.HttpStatusCode.Forbidden,
+ ErrorCode = Protocol.BearerTokenErrorCodes.InsufficientScope,
+ Scope = requiredScopes,
+ };
+ return message;
+ }
/// <summary>
- /// Gets or sets the user endpoint.
+ /// Ensures the message is valid.
/// </summary>
- /// <value>The user endpoint.</value>
- [MessagePart("user-uri")]
- internal Uri UserEndpoint { get; set; }
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+
+ // Make sure the characters used in the supplied parameters satisfy requirements.
+ VerifyErrorCodeOrDescription(this.ErrorCode, Protocol.BearerTokenUnauthorizedResponseParameters.ErrorCode);
+ VerifyErrorCodeOrDescription(this.ErrorDescription, Protocol.BearerTokenUnauthorizedResponseParameters.ErrorDescription);
+ VerifyErrorUri(this.ErrorUri);
+
+ // Ensure that at least one parameter is specified, as required in the spec.
+ ErrorUtilities.VerifyProtocol(
+ this.ErrorCode != null || this.ErrorDescription != null || this.ErrorUri != null || this.Realm != null || this.Scope != null,
+ OAuthStrings.BearerTokenUnauthorizedAtLeastOneParameterRequired);
+ }
/// <summary>
- /// Gets or sets the token endpoint.
+ /// Ensures the error or error_description parameters contain only allowed characters.
/// </summary>
- /// <value>The token endpoint.</value>
- [MessagePart("token-uri")]
- internal Uri TokenEndpoint { get; set; }
+ /// <param name="value">The argument.</param>
+ /// <param name="parameterName">The name of the parameter being validated. Used when errors are reported.</param>
+ private static void VerifyErrorCodeOrDescription(string value, string parameterName) {
+ if (value != null) {
+ for (int i = 0; i < value.Length; i++) {
+ // The allowed set of characters comes from http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#authn-header
+ char ch = value[i];
+ if (!((ch >= '\x20' && ch <= '\x21') || (ch >= '\x23' && ch <= '\x5B') || (ch >= '\x5D' && ch <= '\x7E'))) {
+ ErrorUtilities.ThrowProtocol(OAuthStrings.ParameterContainsIllegalCharacters, parameterName, ch);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Ensures the error_uri parameter contains only allowed characters and is an absolute URI.
+ /// </summary>
+ /// <param name="valueUri">The absolute URI.</param>
+ private static void VerifyErrorUri(Uri valueUri) {
+ if (valueUri != null) {
+ ErrorUtilities.VerifyProtocol(valueUri.IsAbsoluteUri, OAuthStrings.AbsoluteUriRequired);
+ string value = valueUri.AbsoluteUri;
+ for (int i = 0; i < value.Length; i++) {
+ // The allowed set of characters comes from http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#authn-header
+ char ch = value[i];
+ if (!(ch == '\x21' || (ch >= '\x23' && ch <= '\x5B') || (ch >= '\x5D' && ch <= '\x7E'))) {
+ ErrorUtilities.ThrowProtocol(OAuthStrings.ParameterContainsIllegalCharacters, Protocol.BearerTokenUnauthorizedResponseParameters.ErrorUri, ch);
+ }
+ }
+ }
+ }
}
}