//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth2.Messages {
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2.ChannelElements;
using Validation;
///
/// A direct response sent in response to a rejected Bearer access token.
///
///
/// This satisfies the spec in: http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#authn-header
///
public class UnauthorizedResponse : MessageBase, IHttpDirectResponse {
///
/// The headers in the response message.
///
private readonly WebHeaderCollection headers = new WebHeaderCollection();
///
/// Initializes a new instance of the class.
///
/// The protocol version.
protected UnauthorizedResponse(Version version = null)
: base(version ?? Protocol.Default.Version) {
}
///
/// Initializes a new instance of the class.
///
/// The request.
protected UnauthorizedResponse(IDirectedProtocolMessage request)
: base(request) {
}
#region IHttpDirectResponse Members
///
/// Gets or sets the HTTP status code that the direct response should be sent with.
///
public HttpStatusCode HttpStatusCode { get; set; }
///
/// Gets the HTTP headers to add to the response.
///
/// May be an empty collection, but must not be null.
public WebHeaderCollection Headers {
get { return this.headers; }
}
#endregion
///
/// Gets or sets the well known error code.
///
/// One of the values from .
[MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.ErrorCode)]
public string ErrorCode { get; set; }
///
/// Gets or sets a human-readable explanation for developers that is not meant to be displayed to end users.
///
[MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.ErrorDescription)]
public string ErrorDescription { get; set; }
///
/// Gets or sets an absolute URI identifying a human-readable web page explaining the error.
///
[MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.ErrorUri)]
public Uri ErrorUri { get; set; }
///
/// Gets or sets the realm.
///
/// The realm.
[MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.Realm)]
public string Realm { get; set; }
///
/// Gets or sets the scope.
///
/// The scope.
[MessagePart(Protocol.BearerTokenUnauthorizedResponseParameters.Scope, Encoder = typeof(ScopeEncoder))]
public HashSet Scope { get; set; }
///
/// Gets the scheme to use in the WWW-Authenticate header.
///
internal virtual string Scheme {
get { return Protocol.BearerHttpAuthorizationScheme; }
}
///
/// Initializes a new instance of the class
/// to inform the client that the request was invalid.
///
/// The exception.
/// The version of OAuth 2 that is in use.
/// The error message.
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;
}
///
/// Initializes a new instance of the class
/// to inform the client that the bearer token included in the request was rejected.
///
/// The request.
/// The exception.
/// The error message.
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;
}
///
/// Initializes a new instance of the class
/// to inform the client of the required set of scopes required to perform this operation.
///
/// The request.
/// The set of scopes required to perform this operation.
/// The error message.
internal static UnauthorizedResponse InsufficientScope(IDirectedProtocolMessage request, HashSet 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;
}
///
/// Ensures the message is valid.
///
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);
}
///
/// Ensures the error or error_description parameters contain only allowed characters.
///
/// The argument.
/// The name of the parameter being validated. Used when errors are reported.
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);
}
}
}
}
///
/// Ensures the error_uri parameter contains only allowed characters and is an absolute URI.
///
/// The absolute URI.
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);
}
}
}
}
}
}