Class AuthorizationServerConfigurer

java.lang.Object
com.codename1.backend.security.SecurityConfigurer
com.codename1.backend.security.AuthorizationServerConfigurer

public final class AuthorizationServerConfigurer extends SecurityConfigurer

Makes the server an OAuth2 authorization server and OpenID Connect provider.

@Bean
SecurityFilterChain web(HttpSecurity http) {
    http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .formLogin(Customizer.withDefaults())
        .authorizationServer(Customizer.withDefaults());
    return http.build();
}

@Bean
RegisteredClientRepository clients() {
    return new InMemoryRegisteredClientRepository(RegisteredClient.withId("app")
            .clientId("acme-app")
            .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .redirectUri("com.acme.app:/oauth2redirect")
            .scope("openid").scope("profile").build());
}

Endpoints

Path
GET /oauth2/authorize the authorization endpoint, for the signed-in user
POST /oauth2/token the token endpoint
GET /oauth2/jwks the public halves of the signing keys
POST /oauth2/revoke revocation (RFC 7009)
POST /oauth2/device_authorization starts a device grant (RFC 8628)
/oauth2/device_verification the page where the signed-in user types the device's code, and the same steps as JSON for a request that asks for it
GET /userinfo the user's claims, for an access token granted openid
GET /.well-known/openid-configuration, /.well-known/oauth-authorization-server the metadata

The paths can be changed, and the issuer must be given outside a development profile; see AuthorizationServerSettings.

Signing in

The authorization endpoint and the device verification page are for a signed-in user, and how a user signs in is whatever else the chain declares: formLogin, oauth2Login, a second factor. A browser that arrives signed out is sent to sign in and comes back to the request it made. A client that is not a browser -- its Accept does not name text/html -- is answered 401 with login_required instead.

Grants

  • authorization_code, with PKCE: required of a public client, and S256 only. A code works once, for five minutes; presenting one again later revokes what its use was issued. (Presented twice within ten seconds -- a retry, or two processes handed one request -- one is answered and the other refused, and nothing is revoked.) The redirect address must be one the client registered, exactly -- see RegisteredClient -- and a request whose client or address is wrong is answered with a 400 and never a redirect.
  • refresh_token: opaque, stored as a hash, replaced on every use; using one that was replaced revokes the grant, with the same ten seconds' allowance for a client that refreshes twice at once. A public client is issued one too -- the rotation is what protects it there.
  • client_credentials, for a client with a secret.
  • urn:ietf:params:oauth:grant-type:device_code: the device shows a code of eight letters, the user types it at the verification page, is asked -- by name -- whether to let that client in, and approves; the answer counts only from the question this server put to that session. The device's polling is answered authorization_pending, slow_down, access_denied or expired_token until it is.

A client is never granted a scope it was not registered with, and there is no consent page: registering a client with a scope is the consent.

Tokens and keys

An access token is a JWT of type at+jwt (RFC 9068). Its aud is the resource server it is for: what the request named with the resource parameter (RFC 8707) -- at the authorization endpoint, the device authorization endpoint or the token endpoint, where it may only narrow what the grant was made for -- or else the server's default audience, which is the issuer unless AuthorizationServerSettings.Builder.defaultAudience or cn1.security.authorizationserver.audience says otherwise. A client registered with resources may ask for those only; invalid_target answers anything else. /userinfo answers a token that is for this server -- the default audience, or the address of /userinfo named as a resource -- and not one made only for other resource servers. The client the token was issued to is its client_id claim. An ID token is for the client, and its aud is the client id: it carries nonce, auth_time, azp and at_hash, and both are signed RS256 -- or ES256 when the signing key is a P-256 one. The keys are the application's JwkSource bean, or the files named by cn1.security.authorizationserver.jwk.keys; see AuthorizationServerKeys, which is also how an application signs tokens of its own with them.

Access tokens are verified by their signature alone, anywhere: revoking a grant stops its refresh token and its answers at /userinfo, and an access token already issued stays good until it expires. Keep them short.

What is kept, and where

The clients are the application's RegisteredClientRepository bean. The grants are its OAuth2AuthorizationService bean; without one they are kept in this process, which a line at start-up says, and are lost when it stops and unknown to any other process. Client secrets are compared through the application's PasswordEncoder bean, or the one given to clientSecretEncoder(PasswordEncoder). With neither, a server whose clients are all known when it starts -- an InMemoryRegisteredClientRepository -- does not start if one of them has a secret. One whose clients are in a table starts, says once that it cannot check secrets, and refuses a client that presents one with invalid_client.

Not here

A consent page, dynamic client registration, token introspection, opaque access tokens, private_key_jwt and mutual TLS client authentication, pushed authorization requests, DPoP, encrypted tokens, the implicit and password grants, and back-channel or RP-initiated logout.