diff options
7 files changed, 150 insertions, 6 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs index 3eac5a6..500b91d 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs @@ -122,15 +122,15 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { try { var authorizeResult = this.AuthorizationServer.CheckAuthorizeResourceOwnerCredentialGrant( - resourceOwnerPasswordCarrier.UserName, resourceOwnerPasswordCarrier.Password, resourceOwnerPasswordCarrier); + resourceOwnerPasswordCarrier.RequestingUserName, resourceOwnerPasswordCarrier.Password, resourceOwnerPasswordCarrier); if (authorizeResult.IsApproved) { resourceOwnerPasswordCarrier.CredentialsValidated = true; - resourceOwnerPasswordCarrier.UserName = authorizeResult.CanonicalUserName; + resourceOwnerPasswordCarrier.RequestingUserName = authorizeResult.CanonicalUserName; resourceOwnerPasswordCarrier.Scope.ResetContents(authorizeResult.ApprovedScope); } else { Logger.OAuth.ErrorFormat( "Resource owner password credential for user \"{0}\" rejected by authorization server host.", - resourceOwnerPasswordCarrier.UserName); + resourceOwnerPasswordCarrier.RequestingUserName); throw new TokenEndpointProtocolException(accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidGrant, AuthServerStrings.InvalidResourceOwnerPasswordCredential); } } catch (NotSupportedException) { diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs index e17a6a1..e9c952d 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs @@ -251,7 +251,7 @@ namespace DotNetOpenAuth.OAuth2 { Requires.NotNull(password, "password"); var request = new AccessTokenResourceOwnerPasswordCredentialsRequest(this.AuthorizationServer.TokenEndpoint, this.AuthorizationServer.Version) { - UserName = userName, + RequestingUserName = userName, Password = password, }; diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs index 9ab6837..e405a85 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs @@ -48,6 +48,23 @@ namespace DotNetOpenAuth.OAuth2.Messages { IAccessTokenResult IAccessTokenRequestInternal.AccessTokenResult { get; set; } /// <summary> + /// Gets the username of the authorizing user, when applicable. + /// </summary> + /// <value> + /// A non-empty string; or <c>null</c> when no user has authorized this access token. + /// </value> + public virtual string UserName { + get { + IAccessTokenRequestInternal request = this; + if (request.AccessTokenResult != null && request.AccessTokenResult.AccessToken != null) { + return request.AccessTokenResult.AccessToken.User; + } + + return null; + } + } + + /// <summary> /// Gets the type of the grant. /// </summary> /// <value>The type of the grant.</value> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs index 52e65be..02859e1 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// Gets the name on the account whose data on the resource server is accessible using this authorization. /// </summary> string IAuthorizationDescription.User { - get { return this.UserName; } + get { return this.RequestingUserName; } } /// <summary> @@ -64,6 +64,16 @@ namespace DotNetOpenAuth.OAuth2.Messages { #endregion /// <summary> + /// Gets the username of the authorizing user, when applicable. + /// </summary> + /// <value> + /// A non-empty string; or <c>null</c> when no user has authorized this access token. + /// </value> + public override string UserName { + get { return base.UserName ?? this.RequestingUserName; } + } + + /// <summary> /// Gets the type of the grant. /// </summary> /// <value>The type of the grant.</value> @@ -76,7 +86,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> /// <value>The username on the user's account.</value> [MessagePart(Protocol.username, IsRequired = true)] - internal string UserName { get; set; } + internal string RequestingUserName { get; set; } /// <summary> /// Gets or sets the user's password. diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs index 8932cd3..6f7ba7d 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs @@ -48,6 +48,23 @@ namespace DotNetOpenAuth.OAuth2.Messages { IAccessTokenResult IAccessTokenRequestInternal.AccessTokenResult { get; set; } /// <summary> + /// Gets the username of the authorizing user, when applicable. + /// </summary> + /// <value> + /// A non-empty string; or <c>null</c> when no user has authorized this access token. + /// </value> + string IAccessTokenRequest.UserName { + get { + IAccessTokenRequestInternal request = this; + if (request.AccessTokenResult != null && request.AccessTokenResult.AccessToken != null) { + return request.AccessTokenResult.AccessToken.User; + } + + return null; + } + } + + /// <summary> /// Gets a value indicating whether the client requesting the access token has authenticated itself. /// </summary> /// <value> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequest.cs index 65378f9..81acb77 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequest.cs @@ -30,6 +30,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { string ClientIdentifier { get; } /// <summary> + /// Gets the username of the authorizing user, when applicable. + /// </summary> + /// <value>A non-empty string; or <c>null</c> when no user has authorized this access token.</value> + string UserName { get; } + + /// <summary> /// Gets the scope of operations the client is allowed to invoke. /// </summary> HashSet<string> Scope { get; } diff --git a/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs b/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs index 3dc3839..e1af8a5 100644 --- a/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { using System.Text; using System.Threading.Tasks; using DotNetOpenAuth.OAuth2; + using DotNetOpenAuth.OAuth2.ChannelElements; using DotNetOpenAuth.OAuth2.Messages; using Moq; using NUnit.Framework; @@ -106,6 +107,99 @@ namespace DotNetOpenAuth.Test.OAuth2 { } [Test] + public void CreateAccessTokenSeesAuthorizingUserResourceOwnerGrant() { + var authServerMock = CreateAuthorizationServerMock(); + authServerMock + .Setup(a => a.CheckAuthorizeResourceOwnerCredentialGrant(ResourceOwnerUsername, ResourceOwnerPassword, It.IsAny<IAccessTokenRequest>())) + .Returns<string, string, IAccessTokenRequest>((un, pw, req) => { + var response = new AutomatedUserAuthorizationCheckResponse(req, true, ResourceOwnerUsername); + Assert.That(req.UserName, Is.EqualTo(ResourceOwnerUsername)); + return response; + }); + var coordinator = new OAuth2Coordinator<WebServerClient>( + AuthorizationServerDescription, + authServerMock.Object, + new WebServerClient(AuthorizationServerDescription), + client => { + var authState = new AuthorizationState(TestScopes) { + Callback = ClientCallback, + }; + var result = client.ExchangeUserCredentialForToken(ResourceOwnerUsername, ResourceOwnerPassword, TestScopes); + Assert.That(result.AccessToken, Is.Not.Null); + }, + server => { + server.HandleTokenRequest().Respond(); + }); + coordinator.Run(); + } + + [Test] + public void CreateAccessTokenSeesAuthorizingUserClientCredentialGrant() { + var authServerMock = CreateAuthorizationServerMock(); + authServerMock + .Setup(a => a.CheckAuthorizeClientCredentialsGrant(It.IsAny<IAccessTokenRequest>())) + .Returns<IAccessTokenRequest>(req => { + Assert.That(req.UserName, Is.Null); + return new AutomatedAuthorizationCheckResponse(req, true); + }); + var coordinator = new OAuth2Coordinator<WebServerClient>( + AuthorizationServerDescription, + authServerMock.Object, + new WebServerClient(AuthorizationServerDescription), + client => { + var authState = new AuthorizationState(TestScopes) { + Callback = ClientCallback, + }; + var result = client.GetClientAccessToken(TestScopes); + Assert.That(result.AccessToken, Is.Not.Null); + }, + server => { + server.HandleTokenRequest().Respond(); + }); + coordinator.Run(); + } + + [Test] + public void CreateAccessTokenSeesAuthorizingUserAuthorizationCodeGrant() { + var authServerMock = CreateAuthorizationServerMock(); + authServerMock + .Setup(a => a.IsAuthorizationValid(It.IsAny<IAuthorizationDescription>())) + .Returns<IAuthorizationDescription>(req => { + Assert.That(req.User, Is.EqualTo(ResourceOwnerUsername)); + return true; + }); + var refreshTokenSource = new TaskCompletionSource<string>(); + var coordinator = new OAuth2Coordinator<WebServerClient>( + AuthorizationServerDescription, + authServerMock.Object, + new WebServerClient(AuthorizationServerDescription), + client => { + try { + var authState = new AuthorizationState(TestScopes) { + Callback = ClientCallback, + }; + client.PrepareRequestUserAuthorization(authState).Respond(); + var result = client.ProcessUserAuthorization(); + Assert.That(result.AccessToken, Is.Not.Null.And.Not.Empty); + Assert.That(result.RefreshToken, Is.Not.Null.And.Not.Empty); + refreshTokenSource.SetResult(result.RefreshToken); + } catch { + refreshTokenSource.TrySetCanceled(); + } + }, + server => { + var request = server.ReadAuthorizationRequest(); + Assert.That(request, Is.Not.Null); + server.ApproveAuthorizationRequest(request, ResourceOwnerUsername); + server.HandleTokenRequest().Respond(); + var authorization = server.DecodeRefreshToken(refreshTokenSource.Task.Result); + Assert.That(authorization, Is.Not.Null); + Assert.That(authorization.User, Is.EqualTo(ResourceOwnerUsername)); + }); + coordinator.Run(); + } + + [Test] public void ClientCredentialScopeOverride() { var clientRequestedScopes = new[] { "scope1", "scope2" }; var serverOverriddenScopes = new[] { "scope1", "differentScope" }; |