Skip to content

Setup an SPA PKCE client by code on the IdServer, missing documentation (I suppose it is just this) #937

@paillave

Description

@paillave

Using simpleidserver 6, I am trying to implement a dedicated Id server for an SPA application using PKCE.

This SPA authenticates with no pb if it is setup for other Id servers like Azure EntraId or Keycloack. So it should not be an issue at client side. For information the client lib is oidc-client-ts.

The whole authentication process seems to work smoothly: it redirects to the Id server that accepted the authentication request. The user can fill in his authentication information that is accepted, and he is redirected to the source application with a success status.
The issue come when the client application request the final access_token against the /token endpoint (once again, I don't do this manually, as I let oidc-client-ts do the whole job for me and works veryu well with other IdServers).
The request to get the token seems fine:

Image Image but everytime I get this: Image

Till version 4.x.x, TokenEndPointAuthMethod supported 'pkce' as there was an implementation of IOAuthClientAuthenticationHandler that had AuthMethod with this value. Since v5.x.x, I don't see anywhere in your source code that mention of pkce, so I'm confused as it is explicitly mentionned on the documentation of v6.x.x that SPA with PKCE is supported. Could anybody tell me how to setup the server by code to permit an SPA client application to authenticate using PKCE?

Here is my Program.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using SimpleIdServer.IdServer.Builders;
using SimpleIdServer.IdServer.Config;
using SimpleIdServer.IdServer.Domains;
using System.Collections.Generic;

var users = new List<User>
{
    UserBuilder.Create("administrator", "password", "Administrator").SetEmail("[email protected]").SetFirstname("Administrator").Build()
};
var clients = new List<Client>
{
    CreateClient("fp-client", "FP",["https://localhost:44417"],
        [
            ScopeBuilder.CreateApiScope("api://fp.backend/request", true) .Build(),
            ScopeBuilder.CreateApiScope( "email", true).Build(),
            ScopeBuilder.CreateApiScope( "profile", true).Build()
        ]),
    // THIS DOESN'T WORK WITH AS IT REQUIRES A SECRET, WHICH IS NOT COMPLIANT WITH 
    // PKCE STANDARDS AND DOESN'T MAKE SENSE FOR A PUBLIC CLIENT
    // BUT I SUPPOSE "TRADITIONAL WEBSITE CLIENT" MEANS "TRADITIONAL SERVER-SIDE 
    // WEBSITE CLIENT"
    // ClientBuilder
    //     .BuildTraditionalWebsiteClient("fp-client", "secret", null, "https://localhost:44417") 
    //     .AddScope([
    //         ScopeBuilder.CreateApiScope("api://fp.backend/request", true) .Build(),
    //         ScopeBuilder.CreateApiScope( "email", true).Build(),
    //         ScopeBuilder.CreateApiScope( "profile", true).Build()
    //     ])
    //     .DisableConsent()
    //     .Build(),
    
};
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.AllowAnyOrigin()
                                                       .AllowAnyMethod()
                                                       .AllowAnyHeader()));
builder.AddSidIdentityServer()
    .AddInMemoryClients(clients)
    .AddDeveloperSigningCredential()
    .AddInMemoryUsers(users)
    .AddInMemoryLanguages(DefaultLanguages.All)
    .AddPwdAuthentication(true);

var app = builder.Build();
app.UseCors();
app.Services.SeedData();
app.UseSid();
app.Run();



Client CreateClient(string clientId, string clientName, string[] redirectUrls, Scope[] scopes)
{
    var client = ClientBuilder
        .BuildTraditionalWebsiteClient(clientId, "secret", null, redirectUrls)
        .AddScope(scopes)
        .DisableConsent()
        .SetClientName(clientName)
        .Build();

    // I CLEAR SECRETS, HOPING THE SECRETS WON'T BE CHECKED BUT STILL, VALIDATION SEEMS TO FAIL
    client.Secrets.Clear(); 
    
    // HERE, I TRIED EVERYTHING THAT WE CAN SEE IN ANY IMPLEMENTATION 
    // OF `IOAuthClientAuthenticationHandler.AuthMethod`. NONE OF THEM WORKS EITHER
    // client.TokenEndPointAuthMethod = OAuthClientSecretPostAuthenticationHandler.AUTH_METHOD; 

    //EVEN WITHOUT THIS IT FAILS
    client.GrantTypes = ["authorization_code"];
    
    //EVEN WITHOUT THIS IT FAILS
    client.ResponseTypes = ["code"];
    return client;
}

Metadata

Metadata

Labels

questionFurther information is requested

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions