Skip to content
This repository has been archived by the owner on Dec 14, 2017. It is now read-only.

How to use together with other token validation middleware? #92

Closed
up2pixy opened this issue May 31, 2016 · 16 comments
Closed

How to use together with other token validation middleware? #92

up2pixy opened this issue May 31, 2016 · 16 comments
Labels

Comments

@up2pixy
Copy link

up2pixy commented May 31, 2016

Hi,
I'm sorry for the dumb question. I'm trying to make my API valid the token issued by IdentityServer3(provided by 3rd party) and the JWT from Azure AD, and the token from MSA, and the token issued by the API's own simple OAuth Authorization Server. So I'm like
app.UseOAuthAuthorizationServer(...)
app.UseMicrosoftAccountAuthentication(...)
app.UseWindowsAzureActiveDirectoryBearerAuthentication(...)
app.UseIdentityServerBearerTokenAuthentication(...)

The token validation was working until I added the last line above. It looks like the Authorization part in http request header is always caught by the IdentityServer3.AccessTokenValidation MW .

The token issued by local AS is no longer valid. I got the error 500 with following message when I provide the bearer token issued by local AS:

   InvalidOperationException: Sequence contains more than one element]
   System.Linq.Enumerable.SingleOrDefault(IEnumerable`1 source) +516
   Microsoft.Owin.Security.<AuthenticateAsync>d__8.MoveNext() +279
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   IdentityServer3.AccessTokenValidation.<Invoke>d__0.MoveNext() in c:\local\identity\server3\AccessTokenValidation\source\AccessTokenValidation\Plumbing\ScopeRequirementMiddleware.cs:60
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   IdentityServer3.AccessTokenValidation.<Invoke>d__8.MoveNext() in c:\local\identity\server3\AccessTokenValidation\source\AccessTokenValidation\IdentityServerBearerTokenValidationMiddleware.cs:123
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<RunApp>d__5.MoveNext() +203
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.AspNet.Identity.Owin.<Invoke>d__0.MoveNext() +450
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.AspNet.Identity.Owin.<Invoke>d__0.MoveNext() +450
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<RunApp>d__5.MoveNext() +203
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +181
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +69
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<DoFinalWork>d__2.MoveNext() +193
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +96
   System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +509
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +146

And if I provide the token from Azure AD, I will get error 403 with cookie:
WWW-Authenticate: Bearer error="insufficient_scope"

Could you please tell me how to make all validation methods work together? Thanks!

@leastprivilege
Copy link
Member

I'd remove the required scopes property on our middleware and see if that changes anything.

@up2pixy
Copy link
Author

up2pixy commented Jun 1, 2016

Hi @leastprivilege , thank you for replying. After I commented out the required scope line, the AAD JWT is valid but the request with token issued from local simple auth server still return error 500, with a little different error:

ExceptionMessage=Sequence contains more than one element
ExceptionType=System.InvalidOperationException
Message=An error has occurred.
StackTrace=
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at Microsoft.Owin.Security.AuthenticationManager.<AuthenticateAsync>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.HostAuthenticationFilter.<AuthenticateAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Web.Http.Controllers.AuthenticationFilterResult.<ExecuteAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()

Please help...

@leastprivilege
Copy link
Member

did you set the authentication type to a unique value on each MW? That's an OWIN requirement.

@up2pixy
Copy link
Author

up2pixy commented Jun 7, 2016

Ah! It works! Thank you very much @leastprivilege !!!

@up2pixy up2pixy closed this as completed Jun 7, 2016
@up2pixy up2pixy reopened this Jun 8, 2016
@up2pixy
Copy link
Author

up2pixy commented Jun 8, 2016

@leastprivilege Sorry but I need reopen this issue. I can use multiple MW now with unique AuthenticationType name. But the "insufficient_scope" issue is not resolved... Now the AAD is working with AccessTokenValidation MW, but the local auth server is still not working.

As a workaround I added "scope" claim with same required value when I issue token from local auth server. Then when I bring the token to the api, 2 identical identities are added. I believe one is from "UseOAuthBearerAuthentication", one is from "UseIdentityServerBearerTokenAuthentication"
Is there anything wrong when I build my own ApplicationOAuthProvider? or it's a issue of AccessTokenValidation MW?

@leastprivilege
Copy link
Member

Are you sure that each middleware has a unique authentication type?

@up2pixy
Copy link
Author

up2pixy commented Jun 13, 2016

When I stop the pipeline by breaking at next.Invoke(); I see the authentication type is different.. So I think it's different? Here is what I set in the ConfigureAuth

app.UseOAuthAuthorizationServer(
    new OAuthAuthorizationServerOptions
    {
        AllowInsecureHttp = false,
        TokenEndpointPath = new PathString("/Token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(7),
        Provider = new ApplicationOAuthProvider("self"),
        AuthenticationType = "LocalBearer",
    });

app.UseOAuthBearerAuthentication(
    new OAuthBearerAuthenticationOptions
    {
        AuthenticationType = "LocalBearer"
    });

app.UseIdentityServerBearerTokenAuthentication(
    new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = "https://login.3rdpartyIS3server.fake/",
        AuthenticationType = "IS3Bearer",
        RequiredScopes = new[] { "test-scope" },
        ClientId = ConfigurationManager.AppSettings["IS3:ClientId"],
        ClientSecret = ConfigurationManager.AppSettings["IS3:ClientSecret"],
        NameClaimType = "sub",
    });

Please help..

@leastprivilege
Copy link
Member

The scope validation should only kick in if the IS3Bearer middleware produces a result. I think there is even a test for it in the solution. This confuses me.

@up2pixy
Copy link
Author

up2pixy commented Jun 15, 2016

This is weird.. When I bring the token issue by my own ApplicationOAuthProvider("self") I still get the result at line 60 in ScopeRequirementMiddleware.cs
var result = await context.Authentication.AuthenticateAsync(_options.AuthenticationType);
Any clue to debug this? Something wrong in my ApplicationOAuthProvider?

@leastprivilege
Copy link
Member

does the problem only exist for your own oauth provider - or also for AAD tokens?

@up2pixy
Copy link
Author

up2pixy commented Jun 16, 2016

The problem only exists for my own oauth provider... But when I check the token got from AAD, the AuthenticationType is "JWT" but not the value "AADBearer" I set in the middleware... I'm wondering if it's related?

@leastprivilege
Copy link
Member

did you ever resolve that?

@up2pixy
Copy link
Author

up2pixy commented Jul 5, 2016

No I haven't.... Not sure if it's my ApplicationOAuthProvider issue. Would you like to take a look?

    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        private readonly string _publicClientId;

        public ApplicationOAuthProvider(string publicClientId)
        {
            if (publicClientId == null)
            {
                throw new ArgumentNullException("publicClientId");
            }

            _publicClientId = publicClientId;
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

            ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, context.Scope[0], "LocalBearer");
            ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, context.Scope[0], CookieAuthenticationDefaults.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(user.UserName);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }

        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            // Resource owner password credentials does not provide a client ID.
            if (context.ClientId == null)
            {
                context.Validated();
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == _publicClientId)
            {
                Uri expectedRootUri = new Uri(context.Request.Uri, "/");

                if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                {
                    context.Validated();
                }
            }

            return Task.FromResult<object>(null);
        }

        public static AuthenticationProperties CreateProperties(string userName)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
            {
                { "userName", userName }
            };
            return new AuthenticationProperties(data);
        }
    }

@leastprivilege
Copy link
Member

anything you want to add? Or can I close this?

@up2pixy
Copy link
Author

up2pixy commented Sep 13, 2016

I didn't resolve this... Now I disabled my own OAuth provider to bypass this issue...

@koolraap
Copy link

There are two lines in the configuration that say:

AuthenticationType = "LocalBearer"

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants