Go to new doc!

+49 228 5552576-0


info@predic8.com

Example: OAuth2 Authorization Code Flow using Membrane

Membrane API Gateway can act as OAuth2 authorization server and client.


Figure1: The authorization code flow.

Running the Example

In this example we will use Membrane as an authorization server that can authenticate users through a login dialog and a client that communicates with this authentication server on behalf of the user to securely access an unprotected resource.

To run the example go to the $MEMBRANE_HOME/examples/oauth2/membrane folder in the Membrane Service Proxy distribution. Complete the following steps:

  1. Run the service-proxy.bat/.sh in the authorization_server subfolder.
  2. Navigate to http://localhost:7007/.well-known/openid-configuration in your browser to see the openid-discovery file.
  3. Run the service-proxy.bat/.sh in the client subfolder.
  4. Navigate to http://localhost:2000 in your browser to start the login procedure.
  5. Log in with john as username and password as password.
  6. Give consent for sharing information by clicking on accept.
  7. Observe that you get redirected automatically to the resource.

Details

The following part describes the example in detail.

Authorization server

First take a look at the proxies.xml file in the authorization_server subfolder.

        <serviceProxy name="Authorization Server" port="7007">

          <oauth2authserver issuer="http://localhost:7007" location="logindialog" consentFile="consentFile.json">

            <!-- UserDataProvider is exchangeable, e.g. for a database table -->
            <staticUserDataProvider>
              <user username="john" password="password" email="john@predic8.de" />
            </staticUserDataProvider>

            <staticClientList>
              <client clientId="abc" clientSecret="def" callbackUrl="http://localhost:2000/oauth2callback" />
            </staticClientList>

            <!-- Generates tokens in the given format -->
            <bearerToken/>

            <claims value="aud email iss sub username">

              <!-- Scopes are defined from the claims exposed above -->
              <scope id="username" claims="username"/>
              <scope id="profile" claims="username email"/>

            </claims>
          </oauth2authserver>

        </serviceProxy>
      
Listing 1: Authorization Server Config in proxies.xml

You will see a service proxy that listens on port 7000 for incoming calls, in particular it listens for OAuth2 / OpenID Connect calls.

The oauth2authserver element is an implementation of an OAuth2 authorization server.

oauth2authserver

Attributes of oauth2authserver
Attribute Name Description
location Folder where the login dialog index.html resides
issuer The OpenID Connect issuer and is also the base URL for all OAuth2 / OpenID Connect calls.
consentFile JSON file where scopes and claims can be described for usage in the index.html

staticUserDataProvider

The UserDataProvider is used to define information for a specific user. In this example we use a StaticUserDataProvider that enables us to define the information directly in the configuration file.

staticClientList

The ClientList is used to register clients. In this example we use a StaticClientList that enables us to define the information directly in the configuration file.

bearerToken

The TokenGenerator specifies the type of access token that is used in the OAuth2 / OpenID Connect authorization process. Here the BearerTokenGenerator is used.

claims

The last child element specifies the claims and scopes that should be available for the authorization requester. The value attribute specifies the available claims separated by spaces. With those claims one can build scope child elements that define OAuth2 scopes. The scope child elements consist of the id attribute to give the scope a name and the claims attribute to specify the requests claims separated by spaces. Scopes can only contain claims that are available in the value attribute.

Client

Now take a look at the proxies.xml in the client subfolder.

        <serviceProxy name="Resource Service" port="2000">

          <!-- Protects a resource with OAuth2 - blocks on invalid login -->
          <oauth2Resource publicURL="http://localhost:2000/">
            <membrane src="http://localhost:7007" clientId="abc" clientSecret="def" scope="openid profile" claims="username" claimsIdt="sub" />
          </oauth2Resource>

          <!-- Use the information from the authentication server and pass it to the resource server (optional) -->
          <groovy>
          def oauth2 = exc.properties.oauth2
            <!-- Put the eMail into the header X-EMAIL and pass it to the protected server. -->
            exc.request.header.setValue('X-EMAIL',oauth2.userinfo.email)
            CONTINUE
          </groovy>

          <target host="localhost" port="3000"/>

        </serviceProxy>

        <serviceProxy port="3000">
          <groovy>
            exc.setResponse(Response.ok("You accessed the protected resource! Hello " + exc.request.header.getFirstValue("X-EMAIL")).build())
            RETURN
          </groovy>
        </serviceProxy>
      
Listing 2: Client Config in proxies.xml

You will see a service proxy that listens on port 2000 for incoming calls to redirect those to the authorization server that is specified. If incoming calls are not already authorized then the client makes sure to initiate the OAuth2 authorization code flow.

The oauth2Resource element is an implementation of an OAuth2 client.

The publicURL attribute specifies the location the client/resource can be publicly called.

The service proxy on port 3000 simulates the resource that is to be protected against unauthorized invocation.

Note: Port 3000 should be inaccessible from outside in production use else someone with knowledge of the endpoint can call it directly.

membrane

The first and only child element is of base type AuthorizationService and it has several attributes that specify how the OAuth2 calls are done. In this example the MembraneAuthorizationService is used that gets the most part of its configuration from openid-discovery files.

Attribute Name Description
subject Identifier name for a unique, never changing username. Membrane defaults to username but in OpenID Connect subject should be set to sub
src Openid-provider ( in this example this is the issuer attribute in the authorization server ) and gets the openid-discovery file from the issuer
clientId Is given to the user at registration from the authorization server
scope Specifies the requested scopes
claims Individual claims for the request and is only usable with the openid scope. These claims are then available at the userinfo endpoint.
claimsIdt Individual claims for the request and is only usable with the openid scope. These claims are then available in the idToken.

When the OAuth2 / OpenID Connect flow is done everything of importance is collected on the outgoing exchange as a property called oauth2. This property can be used to customize the call to the target e.g. to set the authorization header. In this example we use the Groovy interceptor to put the email address of the user ( that was requested through the profile scope ) as the value of the authorization header.

The oauth2 Exchange Property

The oauth2 property on the exchange object has several properties that can be accessed.

Attribute Name Description
accessToken Access token that is generated by the token endpoint
tokenType Token type of the given access token ( e.g. bearer )
expiresIn Time in seconds the token is valid for
receivedAt The time (Java: LocalDateTime) the token was received
idToken id token that is generated by the token endpoint in openid scope
userinfo Map with the requested claims

Help needed?

Do you need any help for this scenario? Then contact us using the Membrane Google Group or send an email to membrane@predic8.com.