It should actually be possible to have a mobile application querying a backend configured as an OAuth2 client (Spring or not).
An important thing to understand beforehand is that requests to an OAuth2 client are authorized with sessions, not tokens (JWT or not). Such a backend is stateful and vulnerable to CSRF attacks. You'll have to configure your backend to expose a CSRF cookie and manually extract its value in your mobile app to set it as a header (usually X-XSRF-TOKEN
) to each of your POST
, PUT
and DELETE
requests).
Sample authorization_code flow in a web app as you already got working
- the user is redirected to an URI on the client responsible for initiating the flow. Let's say
https://bff.demo.c4-soft.com/oauth2/authorization/keycloak-confidential-user
- what he gets as response is a redirection to the authorization endpoint on the authorization server. Something like:
https://oidc.c4-soft.com/auth/realms/spring-addons/protocol/openid-connect/auth?response_type=code&client_id=spring-addons-bff&scope=openid%20profile%20email%20offline_access%20roles&state=nToh3rnFS-tLVTJ7ajWkq-Fbmi0fUhicoS1nxbdCh8I%3D&redirect_uri=https://bff.demo.c4-soft.com/login/oauth2/code/keycloak-confidential-user&nonce=sNWSLXrLBOfb77jZtImq3k0gV4cg_qKoFLiaEWgbsio
. This URI must be followed with a user agent capable of displaying login UI (login and password, but also maybe multi-factor authentication tokens, etc.)
- the user is redirected back to the client using the
redirect_uri
passed as query parameter (after having a input credentials if he doesn't have a session opened on the authorization server for the client-id and the user-agent used). Let's say https://bff.demo.c4-soft.com/login/oauth2/code/keycloak-confidential-user?state=ZmaSZzNr5WQ8BjyOAMHtnC8SMty1kEMHuQSCLJO4NEE%3D&session_state=1ec90be5-1388-44ac-8b6c-39910558c037&code=c067d4d5-c3eb-4277-9776-9b5c2509b52a.1ec90be5-1388-44ac-8b6c-39910558c037.fb507d1e-2f06-425c-8885-5f232dc02b4e
- the client calls the authorization server endpoint to exchange authorization code for tokens (directly, this does not goes through user device and should require both client-id and client-secret), and stores it in session.
What changes in a native app?
If you redirect the user to the authorization server authorization endpoint with the system browser (or a web view) and let the authorization code flow go to its end normally, the user identity on the backend will be tied to the session for this user agent, not the REST client in your app, and REST requests from your app will remain unauthorized.
The option I just thought of is to modify the request at step 2. above to replace the redirect_uri
with a deep-link to your app. When entering in the app back from authorization server with authorization code, extract the parameters, and follow the redirection as it was before you modify it, but using your native app REST client (not the system browser or a webview). Then, let the flow end normally. This should be enough for the tokens to be tied to your native app REST client session.
Why you should probably not do exactly like that
As stated above, OAuth2 clients are stateful. This poorly scales. Also, you'll run into problems as soon as you leave the single monolithic app model (if you adopt a multi-service architecture).
REST APIs are much better configured as OAuth2 resource servers.
The trick is that some of the social identity providers you list deliver "opaque" access tokens, not JWTs, and introspecting tokens on an authorization server standing in another data center is hardly acceptable. This is the case of Google for instance.
According to OpenID spec, only ID token have to be JWTs. It is safe to send ID tokens around, including to frontends, but the reason for this safety is it should only be used to read user info, not to authorize requests (anyone accessing the ID token can read the userinfo, but shouldn't be able to do anything on behalf of that user using ID tokens).
The audience of the access tokens (by whom it is intended to be used) delivered by each of the providers you list are their own APIs (Google access token for Google APIs, Github access token for Github API, etc.). Not your REST API.
If you want tokens to query your own resource servers, you should configure an authorization server of your own. Choose one with identity federation features ("login with ..."). Keycloak is a famous sample you can install on your desktop and servers. Auth0, Okta and Amazon Cognito are just a few samples among many cloud offers. You could even build your own with spring-authorization-server
.
You then use this authorization server as only reference in your system: resource servers and whatever are your OAuth2 clients (mobile apps configured as "public" clients or Backenf For Frontend configured as "confidential" client (as discussed in the comments to your question).
spring-cloud-gateway
can be configured as BFF with spring-boot-starter-oauth2-client
and TokenRelay=
filter. This gateway will be stateful, but it is a thin layer only responsible for routing and storing OAuth2 tokens. Most part of the processing will be done by stateless resource server behind it.