I'm looking for ideas how to implement two factor authentication (2FA) with spring security OAuth2. The requirement is that the user needs two factor authentication only for specific applications with sensitive information. Those webapps have their own client ids.
One idea that popped in my mind would be to "mis-use" the scope approval page to force the user to enter the 2FA code/PIN (or whatever).
Sample flows would look like this:
Accessing apps without and with 2FA
- User is logged out
- User accesses app A which does not require 2FA
- Redirect to OAuth app, user logs in with username and password
- Redirected back to app A and user is logged in
- User accesses app B which also does not require 2FA
- Redirect to OAuth app, redirect back to app B and user is directly logged in
- User accesses app S which does require 2FA
- Redirect to OAuth app, user needs to additionally provide the 2FA token
- Redirected back to app S and user is logged in
Directly accessing app with 2FA
- User is logged out
- User accesses app S which does require 2FA
- Redirect to OAuth app, user logs in with username and password, user needs to additionally provide the 2FA token
- Redirected back to app S and user is logged in
Do you have other ideas how to apporach this?
I couldn't make the accepted solution work. I have been working on this for a while, and finally I wrote my solution by using the ideas explained here and on this thread "null client in OAuth2 Multi-Factor Authentication"
Here is the GitHub location for the working solution for me: https://github.com/turgos/oauth2-2FA
I appreciate if you share your feedback in case you see any issues or better approach.
Below you can find the key configuration files for this solution.
AuthorizationServerConfig
CustomOAuth2RequestFactory
WebSecurityConfig
TwoFactorAuthenticationFilter
TwoFactorAuthenticationController
So this is how two factor authentication has been implemented finally:
A filter is registered for the /oauth/authorize path after the spring security filter:
This filter checks if the user hasn't already authenticated with a 2nd factor (by checking if the
ROLE_TWO_FACTOR_AUTHENTICATED
authority isn't available) and creates an OAuthAuthorizationRequest
which is put into the session. The user is then redirected to the page where he has to enter the 2FA code:The
TwoFactorAuthenticationController
that handles entering the 2FA-code adds the authorityROLE_TWO_FACTOR_AUTHENTICATED
if the code was correct and redirects the user back to the /oauth/authorize endpoint.A custom
OAuth2RequestFactory
retrieves the previously savedAuthorizationRequest
from the session if available and returns that or creates a new one if none can be found in the session.This custom OAuth2RequestFactory is set to the authorization server like:
When using java config you can create a
TwoFactorAuthenticationInterceptor
instead of theTwoFactorAuthenticationFilter
and register it with anAuthorizationServerConfigurer
withThe
TwoFactorAuthenticationInterceptor
contains the same logic as theTwoFactorAuthenticationFilter
in itspreHandle
method.