Duende BFF Framework - Missing Access Token

894 views Asked by At

I'm trying to authenticate users for access to my React SPA. I'm trying to use IdentityServer6 and Duende's BFF framework to accomplish this. I'm working locally in a debug environment.

I've followed their documentation to the T, and login appears to work, but I keep getting 401 error messages from the BFF Host when I try to call both local and remote endpoints, including the /bff/user (user info) endpoint. The BFF Host logs say that the access token cannot be found.

Here is the log info from the BFF Host:

[23:19:05 Warning] Duende.Bff.Yarp.AccessTokenRequestTransform
Access token is missing. token type: 'Unknown token type', local path: 'Unknown Route', detail: 'Missing access token'

[23:19:05 Information] Yarp.ReverseProxy.Forwarder.HttpForwarder
Not Proxying, a 401 response was set by the transforms.

The logs from the Identity Server, which I believe is issuing the access token correctly on login:

[23:19:04 Information] Duende.IdentityServer.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: Duende.IdentityServer.Endpoints.AuthorizeEndpoint for /connect/authorize

[23:19:04 Information] Duende.IdentityServer.Events.DefaultEventService
{"ClientId": "interactive", "ClientName": null, "RedirectUri": "https://localhost:5002/", "Endpoint": "Authorize", "SubjectId": "1", "Scopes": "openid profile scope2 offline_access", "GrantType": "authorization_code", "Tokens": [{"TokenType": "code", "TokenValue": "****1D-1", "$type": "Token"}], "Category": "Token", "Name": "Token Issued Success", "EventType": "Success", "Id": 2000, "Message": null, "ActivityId": "0HMSBUIP9547T:00000011", "TimeStamp": "2023-07-24T03:19:04.9717252Z", "ProcessId": 49004, "LocalIpAddress": "::1:5001", "RemoteIpAddress": "::1", "$type": "TokenIssuedSuccessEvent"}

[23:19:04 Information] Serilog.AspNetCore.RequestLoggingMiddleware
HTTP GET /connect/authorize responded 302 in 18.3209 ms

[23:19:04 Information] Microsoft.AspNetCore.Hosting.Diagnostics
Request finished HTTP/2 GET https://localhost:5001/connect/authorize?client_id=interactive&redirect_uri=https%3A%2F%2Flocalhost%3A5002%2F&response_type=code&scope=openid%20profile%20scope2%20offline_access&code_challenge=7FjRcNdlHDeguDpLb2CmMSQrFZULfi46FaxRKds-KhY&code_challenge_method=S256&nonce=638257655449622490.ZWVjOGJmYjMtZjdlOC00NjQ1LTgxNTYtM2UxZjdiYjAwMTM0NjNmMjFjYWYtNTA4MC00Yjg4LTgyOWEtMTY5YWIxNDFhMjM4&state=CfDJ8GNxzlZXLyNLt0KDaVzK8HYWEXpzhCOxx2CvvAYf7lImUYvaqSqPOt92ipDF1BQM23yjJ58NcKo7W5m0eftvl3H042zOGJ6mmoBDLjFfjg1o01tH4Y6cE6YxonHxAmr-pkhtas4D0IP-_iRtYqLSPDgquJHpYVqTRAa9HJz6_crmBy0P_0cLHnaU8PmltBADJVH9lF7HWzvkjjbT6MeO8YM92R_mLwhr7uE5UfKqeYV2PxArkamdRKigVBO-gsASJlHSbWa2do2VGhM5itYvKAHJ__IHRAdfoU3CXLSY5ALkUa0Kq9IdP9RANriImkaIfCFaEJHJ3QfvVDAuP529pdIuINXLVi3OHQYZS1O3_bpG&x-client-SKU=ID_NET6_0&x-client-ver=6.21.0.0 - - - 302 0 - 22.6862ms

My BFF Host authentication configuration:

public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
        {
            // add BFF services and server-side session management
            builder.Services.AddBff()
                .AddRemoteApis()
                .AddServerSideSessions();

            builder.Services.AddAuthentication(options =>
                {
                    options.DefaultScheme = "cookie";
                    options.DefaultChallengeScheme = "oidc";
                    options.DefaultSignOutScheme = "oidc";
                })
                .AddCookie("cookie", options =>
                {
                    options.Cookie.Name = "__Host-bff";
                    options.Cookie.SameSite = SameSiteMode.Strict;
                })
                .AddOpenIdConnect("oidc", options =>
                {
                    options.Authority = "https://localhost:5001";
                    options.ClientId = "interactive";
                    options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
                    options.CallbackPath = "/";
                    options.SignedOutCallbackPath = "/";
                    options.ResponseType = "code";
                    options.ResponseMode = "query";

                    options.UsePkce = true;
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.SaveTokens = true;
                    options.MapInboundClaims = false;

                    options.Scope.Clear();
                    options.Scope.Add("openid");
                    options.Scope.Add("profile");
                    options.Scope.Add("scope2");
                    options.Scope.Add("offline_access");

                    options.TokenValidationParameters.NameClaimType = "name";
                    options.TokenValidationParameters.RoleClaimType = "role";
                });

            return builder.Build();
        }

        public static WebApplication ConfigurePipeline(this WebApplication app)
        {
            app.UseHttpsRedirection();
            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseRouting();

            // add CSRF protection and status code handling for API endpoints
            app.UseAuthentication();
            app.UseBff();
            app.UseAuthorization();

            // local API endpoints
            app.MapControllers()
                .RequireAuthorization()
                .AsBffApiEndpoint();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBffManagementEndpoints();
                endpoints.MapRemoteBffApiEndpoint("/api", "https://localhost:7011")
                    .RequireAccessToken(TokenType.User);
            });

            return app;
        }

My Identity Server configuration

public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
        {
            builder.Services.AddRazorPages();

            var isBuilder = builder.Services.AddIdentityServer(options =>
                {
                    options.Events.RaiseErrorEvents = true;
                    options.Events.RaiseInformationEvents = true;
                    options.Events.RaiseFailureEvents = true;
                    options.Events.RaiseSuccessEvents = true;

                    // see https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/
                    options.EmitStaticAudienceClaim = true;
                })
                .AddTestUsers(TestUsers.Users);

            // in-memory, code config
            isBuilder.AddInMemoryIdentityResources(Config.IdentityResources);
            isBuilder.AddInMemoryApiScopes(Config.ApiScopes);
            isBuilder.AddInMemoryClients(Config.Clients);


            // if you want to use server-side sessions: https://blog.duendesoftware.com/posts/20220406_session_management/
            // then enable it
            isBuilder.AddServerSideSessions();

            builder.Services.AddAuthentication();

            return builder.Build();
        }

        public static WebApplication ConfigurePipeline(this WebApplication app)
        {
            app.UseStaticFiles();
            app.UseRouting();
            app.UseIdentityServer();
            app.UseAuthorization();

            app.MapRazorPages()
                .RequireAuthorization();

            return app;
        }

And my Client configurations for my IdentityServer

        public static IEnumerable<Client> Clients =>
            new Client[]
            {
                // interactive client using code flow + pkce
                new Client
                {
                    ClientId = "interactive",
                    ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) },

                    AllowedGrantTypes = GrantTypes.Code,

                    RedirectUris = { "https://localhost:5002/" },
                    FrontChannelLogoutUri = "https://localhost:5002/signout-oidc",
                    PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },

                    AllowOfflineAccess = true,
                    AllowedScopes = { "openid", "profile", "scope2", "offline_access" }
                },
            };

Any help is appreciated!

1

There are 1 answers

0
Tanksley On BEST ANSWER

The problem was with my redirect URI. Identity Server was redirecting to the root of my application instead of to the endpoint required to manage the token.

I fixed this by setting the following in my BFF Host Configuration:

options.CallbackPath = "/signin-oidc";
options.SignedOutCallbackPath = "/signout-oidc";

And doing the same in my IdentityServer Client configuration:

RedirectUris = { "https://localhost:5002/signin-oidc" }
FrontChannelLogoutUri = "https://localhost:5002/signout-oidc",
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },