Search code examples
c#asp.net-web-apioauthopenididentityserver3

IdentityServer BestPractive Combine Implicit and RessourceOwner Workflow


Given: A Identity Server which supports implicit and Resource Owner flow. (Config see below)

An Api that uses IdentityServerBearerTokenAuthentication. So it basiucally trades a password for a token which is used for auth

A UI which uses implicit workflow for auth.

Now I'm able to get a bearer token from the identity server with which I can access the protected api method.

Also as a User I'm able to log in with the implicit flow and see protected views.

Problem The Problems comes when a signed in WebFrontEndUser wants to access a protected API.

The User signs in in the ui with the implicit flow. After he is authenticated he tries to access a protected api. The Api returns that he is not authorized.

How can I configure the environment so that the api uses the openid information from the user cookie?

Website Confing

   app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            });

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                ClientId = "foo_implicit",
                Authority = identServer,
                RedirectUri = "http://localhost/foo/",
                ResponseType = "token id_token",
                Scope = "openid profile",
                SignInAsAuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
            });

WebApi Config

app.UseIdentityServerBearerTokenAuthentication(new IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenAuthenticationOptions()
        {
            Authority = identServer
        });

IdentityServer Client Config

  new Client
                {
                    Enabled = true,
                    ClientId = "foo_implicit",
                    ClientName = "foo Site",
                    ClientSecrets = new List<Secret>
                    {
                        new Secret("foo".Sha256())
                    },
                    Flow = Flows.Implicit,
                    AllowedScopes = new List<string>
                    {
                        Constants.StandardScopes.OpenId,
                        Constants.StandardScopes.Profile,
                        "read"
                    },
                    RedirectUris = new List<string>()
                    {
                        "http://localhost/foo/"
                    }
                },
                new Client
                {
                    Enabled = true,
                    ClientId = "foo",
                    ClientName = "foo api",
                    ClientSecrets = new List<Secret>
                    {
                        new Secret("foo".Sha256())
                    },
                    Flow = Flows.ResourceOwner,
                    AllowedScopes = new List<string>
                    {
                        Constants.StandardScopes.OpenId,
                        "read"
                    }
                }

Solution

  • Using OpenID Connect you can request both Identity and Access Tokens during authentication. You are already getting an access token during authentication (response type token), so I would grab this and use it to access then API. This would prevent the need for a separate Resource Owner client.

    You can grab this access token using the the OpenIdConnectAuthenticationNotifications property of OpenIdConnectAuthenticationOptions, e.g.:

    Notifications = new OpenIdConnectAuthenticationNotifications
    {
        SecurityTokenValidated = x =>
        {
            x.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", x.ProtocolMessage.AccessToken));
            return Task.FromResult(0);
        }
    }
    

    Also your IdentityServerBearerTokenAuthenticationOptions should state one or more Scopes that a token requires for access. Otherwise any access token from that authority can access your API. See documentation for more details on this.

    app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = identServer,
        RequiredScopes = new[] { "api1" }
    });