Search code examples
pythonauthenticationoauth-2.0jwtmicroservices

Proper way of setting up OAuth2 for authentication for microservices


Background information

I have a microservice that handles User authentication via REST API, the frontend microservice contacts this authentication microservice (AMS) and everything works fine and dandy.

Main issue

I want to incorporate Oauth2 based login option and I am kind of lost as to how do I set it up properly for a microservices architecture.

I plan to use a strategy pattern to support Oauth2. Below is my current context that supports the default JWT-based strategy. The plan is to simply get the user to log in via the Oauth2 provider and then store the refresh/access tokens in the database for future logins while continuing to issue my own JWT-based access and refresh tokens for authorisation.

class AuthenticationContext:
    def __init__(self, authentication_service):
        self.authentication_service = authentication_service

    def set_authentication_strategy(self, strategy):
        self.authentication_service = strategy

    def login(self, request):
        return self.authentication_service.login(request)

    def logout(self, request):
        return self.authentication_service.logout(request)

    def validate_token(self, request):
        return self.authentication_service.validate_token(request)

    def refresh(self, request):
        return self.authentication_service.refresh(request)

    def register(self, request):
        return self.authentication_service.register(request)
+... 

Primary concerns

Do I need to set up a frontend that exchanges the authorisation token with the oauth2 provider (let's say Google) and then sends that authorisation token to AMS and then let the AMS use the authorisation token and exchange it for access and refresh tokens from the Oauth2 provider ?

In this case, does it mean I have to register both microservices (frontend and AMS) with the oauth2 provider so that one can request the code and the other can exchange it?

Is it more advisable to bring the frontend's code (React + ChakraUI) for login, and registration to the AMS (python)?

Is there an alternative to both or a right way to do it?

Additional information

I am open to other suggestions and recommendations as well as I am not well-versed in authentication and authorisation in microservices architecture.

This is my current default strategy's sequence diagram.


Solution

  • A quick note that may seem minor but is an important detail:

    • OAuth 2.0 = authorization
    • OIDC (which sits on top of OAuth 2.0) = authentication

    The flow is essentially the same (OIDC adds additional parameters), though, if you are doing authorization only vs authorization & authentication.

    The best practice is currently the authorization code flow. In some cases, it's required to supplement with PKCE, but it doesn't hurt to have it regardless.

    Since you have a backend available, it makes things a little easier as PKCE is not strictly necessary.

    The backend initiates a login by redirecting the frontend/browser/user agent to the OAuth 2.0 provider's authorize endpoint with the authorization request. The user authenticates and authorizes, then they are redirected by the provider back to the redirect URI which should point back to your app (frontend).

    With this redirect, there'll be an authorization code. It should be in the query parameters of the url. The frontend gives this to the backend.

    Next, the backend makes a call directly to the provider to exchange the authorization code for the tokens (access token for authorization and id token for authentication). The tokens should come back directly to your backend in the response.

    Once your backend validates these tokens, either through some endpoint on the provider or by verifying the signature yourself, you now have an authenticated user and access to the resources the user authorized as specified by the scope parameter.