NativeOAuth2LoginService class

The OAuth2LoginService of phones and desktops: the app logs its user in by itself with an OAuth 2.0 or OpenID Connect provider (Keycloak, Google, Auth0, ...), keeps them logged in across restarts, and logs them out.

An app cannot keep a client secret (anyone can extract it), so it logs in as a public client with the authorization code flow and PKCE (RFC 8252, "OAuth 2.0 for Native Apps"): login opens the provider's page in the system's login window (a Safari sheet, a Chrome tab, the default browser), the user logs in there (the app never sees the password), the provider redirects back with a code, and the code is exchanged for tokens. Everything that can be forged is checked: the state against forged redirects, the iss of the redirect against mixed-up providers, the PKCE verifier binds the code to this app, the ID token's issuer, audience, times and nonce, and the user info's subject. The user then is an OAuth2AuthenticationToken in the SecurityContextHolder (so @PreAuthorize and the Feign interceptor see them), the tokens are in the OAuth2AuthorizedClientService (the SecretStoreOAuth2AuthorizedClientService on the platform's secure storage), and an AuthenticationSuccessEvent is published.

restore brings the user back at startup from the stored tokens, refreshing them when needed; logout revokes the tokens at the provider (when it has a revocation_endpoint), forgets them, clears the context and publishes a LogoutSuccessEvent. With DPoP it also deletes the app's PlatformDPoPKey, so the next login is bound to a new key.

A registration with native-sign-in=apple (NativeSignIn; the default of the apple provider) signs in with Sign in with Apple's sheet on iOS and macOS (appleIdSignIn) instead of the browser: the sheet's state must come back, its identity token (from the operating system, not from a TLS connection of the app) must verify with Apple's keys (nativeIdTokenDecoder: the registration's jwk-set-uri, issuer, audience = the client id, expiry) and carry the nonce sent, and its sub must be the sheet's user. The code is then exchanged at the registration's token-uri, which for an app must be its own server: only a server can sign Apple's client secret (Apple's own flow for apps: the app hands the code to its server, which checks it with Apple and keeps the session). The name and e-mail Apple sends on the first authorization only go into the user's info and are kept in the login record (and carried to the next sign-ins of the same user).

Apps use it through OAuth2LoginService: the package declares it as the oauth2LoginService bean on Android, iOS, macOS, Windows and Linux when a registration is configured, with the platform's AuthorizationWindow (PlatformAuthorizationWindow on Android, iOS and macOS, LoopbackAuthorizationWindow on Windows and Linux). A web page gets the ServerOAuth2LoginService instead, where the server logs the user in.

Spring has no counterpart: Spring Security's oauth2Login() logs browsers in on a server. The checks are those of its OAuth2LoginAuthenticationFilter, OidcAuthorizationCodeAuthenticationProvider and OidcUserService.

Implemented types

Constructors

NativeOAuth2LoginService({required ClientRegistrationRepository clientRegistrationRepository, required OAuth2AuthorizedClientService authorizedClientService, required SecretStore secretStore, required OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient, required OAuth2UserService<OAuth2UserRequest, OAuth2User> userService, required OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService, AuthorizationWindow? authorizationWindow, OAuth2AuthorizedClientManager? authorizedClientManager, OAuth2TokenRevocationClient? revocationClient, PlatformDPoPKey? dPoPKey, AppleIdSignIn? appleIdSignIn, Map<String, NativeSignIn> nativeSignIn = const {}, OidcIdTokenDecoder? nativeIdTokenDecoder, OidcIdTokenDecoder? idTokenDecoder, GrantedAuthoritiesMapper authoritiesMapper = const NullAuthoritiesMapper(), ApplicationEventPublisher? eventPublisher, SecurityContextHolderStrategy? securityContextHolderStrategy, Uri? baseUrl})
A login service; the package's auto-configuration passes the beans.

Properties

accessTokenResponseClient → OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>
Exchanges the code.
final
appleIdSignIn → AppleIdSignIn?
Sign in with Apple's sheet (iOS, macOS), or null where there is none: then every registration logs in in the authorizationWindow.
final
authentication → OAuth2AuthenticationToken?
The logged-in user's authentication, or null.
no setteroverride
authenticationChanges → Stream<OAuth2AuthenticationToken?>
The authentication after each login, restore and logout (null): for a UI that shows who is logged in. PrincipalNotifier and context.principal are usually simpler.
no setteroverride
authoritiesMapper → GrantedAuthoritiesMapper
Maps the user's authorities.
final
authorizationWindow → AuthorizationWindow?
Opens the provider's login page and waits for the redirect back, or null (then login throws).
final
authorizedClientManager → OAuth2AuthorizedClientManager?
Refreshes the tokens in restore; without one they are not refreshed there.
final
authorizedClientService → OAuth2AuthorizedClientService
Where the tokens are kept.
final
baseUrl → Uri
The value of {baseUrl} in the redirect URI templates.
final
clientRegistrationRepository → ClientRegistrationRepository
The registrations.
final
dPoPKey → PlatformDPoPKey?
The app's DPoP key, deleted at logout so the next login gets a new one; null when the app has no PlatformDPoPKey (a key of its own is left alone).
final
eventPublisher → ApplicationEventPublisher?
Receives the login and logout events, or null.
final
hashCode → int
The hash code for this object.
no setterinherited
idTokenDecoder → OidcIdTokenDecoder
Decodes and checks the ID token.
final
isLoggedIn → bool
Whether someone is logged in.
no setteroverride
nativeIdTokenDecoder → OidcIdTokenDecoder?
Checks the identity tokens of the sheets, signatures included (a JwkSetOidcIdTokenDecoder in the package's bean); without one a native sign-in is refused.
final
nativeSignIn → Map<String, NativeSignIn>
The registrations that sign in with a sheet of the system instead of the browser, by id (NativeSignIn.of the properties).
final
oidcUserService → OAuth2UserService<OidcUserRequest, OidcUser>
Loads OpenID Connect users.
final
revocationClient → OAuth2TokenRevocationClient?
Revokes the tokens in logout; without one they are not revoked.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
secretStore → SecretStore
Where the login record is kept (the same store as the tokens).
final
securityContextHolderStrategy → SecurityContextHolderStrategy
Where the logged-in user is put.
final
user → OAuth2User?
The logged-in user (OidcUser with OpenID Connect), or null.
no setter
userService → OAuth2UserService<OAuth2UserRequest, OAuth2User>
Loads OAuth 2.0 users.
final

Methods

login(String registrationId, {Map<String, String> additionalParameters = const {}}) → Future<OAuth2AuthenticationToken>
Logs the user in with the registration registrationId, in the system's login window, and returns their authentication.
override
logout({bool endSession = false, Uri? postLogoutRedirectUri}) → Future<void>
Logs the user out: revokes their tokens at the provider (refresh token first, when it has a revocation_endpoint), forgets the tokens, deletes the dPoPKey (the next login makes a new one), clears the security context and publishes a LogoutSuccessEvent. Nothing happens when nobody is logged in.
override
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
restore() → Future<OAuth2AuthenticationToken?>
Brings back the user of the stored tokens and returns their authentication; null when nobody is stored.
override
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Constants

sessionKey → const String
The key of the login record in the secretStore: which registration and user are logged in, and what is needed to show them offline.