Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"unauthorized_client" during login #766

Closed
GMillerVarian opened this issue Jul 10, 2023 · 9 comments
Closed

"unauthorized_client" during login #766

GMillerVarian opened this issue Jul 10, 2023 · 9 comments
Assignees

Comments

@GMillerVarian
Copy link

Which version of Duende BFF are you using?
2.1.1

Which version of .NET are you using?
.NET 6.0

Describe the bug
We took the SplitHosts sample, using the "TODOs API remote", plugged in our own authority and ClientId / ClientSecret and it was working fine. We then tried to use this BackendHost with one of our own sample frontend hosts (Angular) and API host but we receive the following error when trying to login:

dbug: Duende.AccessTokenManagement.ClientCredentialsTokenEndpointService[0]
      Requesting client credentials access token at endpoint: <redacted>
fail: Duende.AccessTokenManagement.ClientCredentialsTokenManagementService[0]
      Error requesting access token for client Duende.TokenManagement.SchemeBasedClient:oidc. Error = unauthorized_client.

We looked at a few other samples but it wasn't clear to us what we could be missing from our frontend.

@josephdecock josephdecock self-assigned this Jul 11, 2023
@josephdecock
Copy link
Member

This error in the BFF is saying that the BFF failed to use the client credentials flow to obtain a token. There should be more details available in the IdentityServer logs about why that happened. Take a look there, see if it helps you, and feel free to follow up here with more questions and the details from IdentityServer's logs.

@GMillerVarian
Copy link
Author

You are correct, it was failing on client credential flow. Since this was a SPA client (not headless) it should be using auth code flow. It was due to the following configuration for our backend API host:

endpoints.MapRemoteBffApiEndpoint("/assessments", "http://localhost:3001/assessments")
    .RequireAccessToken(TokenType.UserOrClient);

Once I changed TokenType to "User" it started working.

Now, it is getting farther. It completes the interactive login flow, and the callback to /sign-oidc completes successfully. But then fails with the following:

dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
      Request matched endpoint '/assessments/{**catch-all}'
dbug: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[2]
      The request has an origin header: 'http://localhost:4200'.
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      CORS policy execution successful.
dbug: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[9]
      AuthenticationScheme: cookie was not authenticated.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint '/assessments/{**catch-all}'
dbug: Duende.AccessTokenManagement.OpenIdConnect.UserAccessAccessTokenManagementService[0]
      No active user. Cannot retrieve token
warn: Duende.Bff.Yarp.AccessTokenRequestTransform[4]
      Access token is missing. token type: 'Unknown token type', local path: 'Unknown Route', detail: 'Missing access token'
info: Yarp.ReverseProxy.Forwarder.HttpForwarder[59]
      Not Proxying, a 401 response was set by the transforms.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint '/assessments/{**catch-all}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/2 GET https://localhost:5001/assessments - - - 401 0 - 71.5753ms

@josephdecock
Copy link
Member

josephdecock commented Jul 14, 2023

This error probably means one of two things:

  • The browser didn't send the auth cookie with the api request, or
  • The ticket stored within the cookie doesn't contain the access token.

I would verify that the network requests are including the cookie and that the SaveTokens option is enabled in the OIDC authentication handler.

@GMillerVarian
Copy link
Author

I've adding some logging and can confirm that the BFF backend is not receiving any cookies. So I will look into that. I do see the BFF cookie in the browser in the SPA.

In the meantime, I've noticed something else that I don't understand. Yesterday, the BFF backend was returning 401 on the API calls (as expected, due to the missing cookie). Today, with no changes (other than adding a middleware for logging the cookies), BFF is forwarding the API calls to the API backend with a valid auth header. I continue see this even if I delete the BFF cookie from the browser. Why would BFF be returning successful API calls with no session cookie? I wouldn't expect it to ever do this. I had seen this before (either by manually deleting the BFF cookie or by calling the logout endpoint) but could then not later reproduce, I believe that time I even saw it refresh the token and provide an updated access token to our API server with no BFF session cookie.

Here is what the log looks like:

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/2 GET https://localhost:5001/assessments - -
*** Number of incoming request cookies: 0 ***
dbug: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[4]
      The request path /assessments does not match a supported file type
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
      1 candidate(s) found for the request path '/assessments'
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]
      Endpoint '/assessments/{**catch-all}' with route pattern '/assessments/{**catch-all}' is valid for the request path '/assessments'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
      Request matched endpoint '/assessments/{**catch-all}'
dbug: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[2]
      The request has an origin header: 'http://localhost:4200'.
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      CORS policy execution successful.
dbug: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[9]
      AuthenticationScheme: cookie was not authenticated.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint '/assessments/{**catch-all}'
dbug: Duende.AccessTokenManagement.OpenIdConnect.UserAccessAccessTokenManagementService[0]
      No active user. Cannot retrieve token
dbug: Duende.AccessTokenManagement.DistributedClientCredentialsTokenCache[0]
      Cache hit for access token for client: Duende.TokenManagement.SchemeBasedClient:oidc
info: Yarp.ReverseProxy.Forwarder.HttpForwarder[9]
      Proxying to http://localhost:3001/assessments HTTP/2 RequestVersionOrLower no-streaming
info: Yarp.ReverseProxy.Forwarder.HttpForwarder[56]
      Received HTTP/1.1 response 200.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint '/assessments/{**catch-all}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/2 GET https://localhost:5001/assessments - - - 200 - application/json;+charset=utf-8 113.6546ms

@GMillerVarian
Copy link
Author

The cookies were not coming through because I was running the front end locally using non-SSL. Once I switched to SSL it started working.

However, I am still seeing the issue where API calls are successfully authenticated using a cached token even after deleting the BFF session cookie.

@josephdecock
Copy link
Member

If you have UserOrClient access token type, then the BFF will use the user's access token if available, and fall back to a client credentials token when it is not available. Are you maybe still using that token type in some cases? I would also recommend that you look at the incoming requests from the point of view of the api, and try to see which token is being sent.

@GMillerVarian
Copy link
Author

Yes, it was using UserOrClient access token type. We are using a SPA client with only "authorization_code" for allowed grant types. I am no longer seeing this behavior (using cached tokens when the session cookie is deleted) today, it's back to the unauthorized_client error that I originally saw. I'm still unclear on when to use UserOrClient token type, or how exactly this works (it seems I've seen different behavior at different times) but am I safe to assume that for SPA clients we will always want to use User token type?

Also, can you comment on whether either of the following are supported, or if there are any plans to support in the future:

  1. websockets, i.e. mapping ws endpoints in additional to https API endpoints
  2. defining multiple OIDC schemes (ex. with different authority and/or client credentials), and deciding which scheme to use based on some condition (ex. incoming URL)

@josephdecock
Copy link
Member

josephdecock commented Jul 17, 2023

If you only have an authorization code grant type for you client, then the user token type is appropriate. The UserOrClient type would make sense if you want to have a fallback that used the client credentials grant, to make a request that is definitely from the client application, but that maybe doesn't have a logged in user yet. That's a fairly unusual circumstance for a spa, so I would think you probably want that user token type.

websocket support isn't directly supported, but I think it might be possible to extend the BFF to do so. You'd probably need to implement a server side session facility so that you could store the tokens somewhere other than the default asp.net auth cookie's data protected payload. A number of folks have asked about using signalr hubs with the BFF, and we'd like to investigate that and create a sample using server side sessions. The backlog item tracking that is here.

In the most recent version of the BFF, we added an additional extensibility point that customizes how tokens are retrieved on a per-endpoint basis, called the IAccessTokenRetriever. You could implement that interface and have it authenticate with different schemes. For more details, see the docs here.

@GMillerVarian
Copy link
Author

OK thank you for all your help.

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

No branches or pull requests

2 participants