Authentication with OAuth2

This tutorial introduces GeoServer OAuth2 support and walks through the process of setting up authentication against an OAuth2 provider. It is recommended that the Authentication chain section be read before proceeding.

OAuth2 Protocol and GeoServer OAuth2 core module

This module allows GeoServer to authenticate against the OAuth2 Protocol.

In order to let the module work, it is mandatory to setup and configure both the oauth2 and oauth2-xxxx-extension (where xxxx is the provider e.g. google, github, openid, geonode). Both files are included in one ZIP file, obtained from the GeoServer build server (change version as necessary) - search for sec-oauth2.

The first one contains the necessary dependencies of the OAuth2 core module. This module contains the implementation of the GeoServer security filter, the base classes for the OAuth2 Token services and the GeoServer GUI panel.

The second one provides the OAuth2 implementation for each provider. Since in almost all cases the only thing different between OAuth2 Providers are the endpoint URIs and the client connection information (not only the keys - public and secret - but also the user profile representations), in order to allow GeoServer connecting to a specific OAuth2 provider it is sufficient to install the OAuth2 Core module plugin (and correctly configure the parameters through the GeoServer GUI - see next section for the details) and the concrete implementation of the OAuth2 REST token template and resource details.

Currently this module is shipped with a sample extension for Google OAuth2 Provider. This is a particular case since the Google JWT response is not standard and therefore we had to define and inject also a GoogleUserAuthenticationConverter taking the Google REST response against a valid access_token and converting it to an OAuth2 standard one.

Other than this the most interesting part is the implementation of the base class GeoServerOAuth2SecurityConfiguration.

The latter contains the Google implementation of the OAuth2RestTemplate.

In the next section we will see how to install and configure the OAuth2 security filter on GeoServer authenticating against Google OAuth2 Provider.

Configure the Google authentication provider

The first thing to do is to configure the OAuth2 Provider and obtain Client ID and Client Secret keys.

  1. Obtain OAuth 2.0 credentials from the Google API Console.

    Visit the Google API Console to obtain OAuth 2.0 credentials such as a client ID and client secret that are known to both Google and your application. The set of values varies based on what type of application you are building. For example, a JavaScript application does not require a secret, but a web server application does.

    • Login with a valid Google Account

      ../../_images/google_api_console001.png
    • Click on API Manager

      ../../_images/google_api_console002.png
    • Click on Credentials

      ../../_images/google_api_console003.png

      Note

      The first time you land here, Google will ask to create at least one project

      ../../_images/google_api_console004.png

      For the purpose of this tutorial we will create a sample project. You are free to create other projects or update existing ones through the Google API Console later.

      ../../_images/google_api_console005.png

      If no Credentials are present, you will be asked to create new one.

      ../../_images/google_api_console006.png
  2. Select an existing (or create a new one) OAuth Client ID

    Click on the Client credentials context menu as shown in the figure below.

    ../../_images/google_api_console007.png
  3. Configure a new Web application

    • If it is the first time you create an OAuth Client ID, you will be asked to create a new consent screen

      ../../_images/google_api_console008.png
    • Customize the consent screen

      Warning

      This step is mandatory only if it’s the first time you are defining a Web application on a new project.

      ../../_images/google_api_console009.png

      Note

      It can be edited and updated also later (see last point of this section below)

    • Select Application type -> Web application

      Warning

      This step is mandatory only if it’s the first time you are defining a Web application on a new project.

      ../../_images/google_api_console010.png
    • Add a Name and the Authorized redirect URIs like shown here below.

      Note

      This sample creates a client working on the default local URL http://localhost:8080/geoserver. Of course this will work only on a local instance and can’t be used for a production system.

      However it is possible to add as many Authorized redirect URIs you need to a new Web application.

      It is also possible to create many Client credentials with customised consent screen and Web application, depending on your specific needs. Every public GeoServer instance (or cluster of GeoServer belonging to a specific project) should have its own specific Client credentials.

      ../../_images/google_api_console011.png

      Note

      Always add two entries for each URI. One without the ending / and another one with it.

      ../../_images/google_api_console012.png
  4. Click on Create and take note of the Client ID and the Client Secret.

    At the end of the procedure Google will show-up a small dialog box with the Client ID and the Client Secret. That info can be always accessed and updated from the Google API Console

    ../../_images/google_api_console013.png
  5. Optionally customize the OAuth consent screen.

    At any time it is possible to update and customize the OAuth consent screen. You can put here your logo, app name, ToS and so on.

    ../../_images/google_api_console014.png

Configure the GeoServer OAuth2 filter

  1. Start GeoServer and login to the web admin interface as the admin user.

  2. Click the Authentication link located under the Security section of the navigation sidebar.

    ../../_images/filter1.jpg
  3. Scroll down to the Authentication Filters panel and click the Add new link.

    ../../_images/filter2.jpg
  4. Click the OAuth2 link.

    ../../_images/filter3.jpg
  5. Fill in the fields of the settings form as follows:

    ../../_images/oauth2chain001.png

    The default values provided with the plugin are valid for the Google OAuth2 Provider and are the following:

    "Enable Redirect Authentication EntryPoint" = False
    "Access Token URI" = https://accounts.google.com/o/oauth2/token
    "User Authorization URI" = https://accounts.google.com/o/oauth2/auth
    "Redirect URI" = http://localhost:8080/geoserver
    "Check Token Endpoint URL" = https://www.googleapis.com/oauth2/v1/tokeninfo
    "Logout URI" = https://accounts.google.com/logout
    "Scopes" = https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile
    

    Note

    1. Client ID and Client Secret are the ones Google provided

    2. Choose a Role Service able to recognize user emails as IDs. By default a connected user will have ROLE_USER role

    Warning

    A few words on the Enable Redirect Authentication EntryPoint option

    This option allows you to decide whether or not to force automatic redirection to OAuth2 Access Token URI or not for authentication.

    What does that mean?

    • Enable Redirect Authentication EntryPoint = True

      If not already authenticated (or no valid Access Token is provided in the query string), this option will force a redirection to the OAuth2 Provider Login page.

      This may cause unwanted behavior since it will override every other explicit login method like form. In other words if the filter is applied for instance to the web endpoint, it won’t be possible to access to the GeoServer Admin GUI using the standard login method via browser.

    • Enable Redirect Authentication EntryPoint = False

      In order to avoid the above issue, by disabling this option you will be forced to use an explicit Authentication Endpoint to login via the OAuth2 Provider login page.

      If not already authenticated (or no valid Access Token is provided in the query string), you must authenticate through the following URLs:

      1. GeoServer OAuth2 Authorization Endpoint; http://<host:port>/geoserver/j_spring_oauth2_login

      2. OAuth2 Provider Explicit User Authorization Endpoint; this must be adapted for your specific OAuth2 Provider, the protocol stated that it should be

        https://<USER_AUTHORIZATION_URI>?scope=<SCOPES>&response_type=code&redirect_uri=<REDIRECT_URI>&client_id=<CLIENT_ID>
        

        For Google OAuth2 Provider is:

        https://accounts.google.com/o/oauth2/auth?scope%3Dhttps://www.googleapis.com/auth/userinfo.email%2Bhttps://www.googleapis.com/auth/userinfo.profile%26response_type%3Dcode%26redirect_uri%3D<REDIRECT_URI>%26client_id%3D<CLIENT_ID>
        
  6. Update the filter chains by adding the new OAuth2 filter.

    Once everything has been configured you should be able to see the new oauth2 filter available among the Authentication Filters list

    ../../_images/oauth2filter001.png

    Through this it will be always possible to modify / update the filter options, or create more of them.

    The next step is to add the filter to the Filter Chains you want to protect with OAuth2 also

    ../../_images/oauth2filter002.png
  7. Select the OAuth2 Filter for each filter chain you want to protect with OAuth2.

    If you need to protect all the GeoServer services and the GeoServer Admin GUI too with OAuth2, you need to add the oauth2 filter to all the following chains

    • web

    • rest

    • gwc

    • default

    The order of the authentication filters depends basically on which method you would like GeoServer to try first.

    Note

    During the authentication process, the authentication filters of a Filter Chain are executed serially until one succeed (for more details please see the section Authentication chain)

    Warning

    If Enable Redirect Authentication EntryPoint = True for OAuth2 Filter, the web chain won’t be able to login through the form method.

    ../../_images/oauth2filter003.png

    Note

    Remember that the anonymous filter must be always the last one.

  8. Save.

    ../../_images/oauth2filter004.png

It’s now possible to test the authentication:

  1. Navigate to the GeoServer home page and log out of the admin account.

  2. Try to login again, you should be able now to see the external Google login form.

    ../../_images/test1.jpg
    ../../_images/test2.jpg
    ../../_images/test3.jpg
    ../../_images/test4.jpg
    ../../_images/test5.jpg

OpenID connect authentication

The OpenID connect authentication is working in a way quite similar to Google (and GitHub) authentications, the only difference is that the authentication page cannot propose default values for the various endpoints, which have to be configured manually.

In case the web login will not be used, the “client ID” and “client secret” are not actually needed, and can be filled with two made up values (the validation just checks they are present, but they will be used only in the “authorisation flow”, but not when doing OGC requests where the client is supposed to have autonomously retrieved a valid bearer token).

The configuration GUI supports OpenID Discovery documents. If the server supports them it’s sufficient to provide the path to the document, or to the authentication service root, and the GUI will auto-fill itself based on the document contents:

../../_images/discovery.png

The UI allow to set also the Post Logout Redirect URI which will be used to populate the post_logout_redirect_uri request param, when doing the global logout from the GeoServer UI. The OpenId provider will use the URI to redirect to the desired app page.

In addition, the OpenID connect authentication is able to extract the user roles from either the ID token or the Access Token:

../../_images/openidconnect-roles.png

The chosen attribute must be present in either the Access Token or in the Id token, and be either a string or an array of strings.

From UI it is also possible to set the Response Mode value. The field can be kept empty but it is needed when the OpenId server used as Identity Provider doesn’t send by default the authorization code as a query string (that is mandatory in order to allow GeoServer and OpenId integration to work properly).

Finally the admin can allow the sending of the client_secret during an access_token request trough the Send Client Secret in Token Request. Some OpenId implementation requires it for the Authorization Code flow when the client app is a confidential client and can safely store the client_secret.

OpenID Connect With Attached Access Bearer Tokens

The OpenID Connect plugin allows the use of Attached Bearer Access Tokens. This is typically used by automated (i.e. desktop or external Web Service) to access the Geoserver REST API.

The setup process is as follows:

  1. Setup your OAuth2 OpenID Connect configuration as normal

  2. On the OpenID Connect configuration screen (bottom), makes sure “Allow Attached Bearer Tokens” is checked

  3. You can not use ID Tokens as a Role Source for the attached Bearer Tokens (see below)

To Use:

  1. Obtain an Access Token from the underlying IDP

  2. Attach the access token to your HTTP request headers

Authorization: Bearer <token>

The Access Token (JWT) is validated;

  1. The Access Token is used to get the “userinfo” endpoint. The underlying IDP will verify the token (i.e. signature and expiry)

  2. The Audience of the Token is checked that it contains the GeoServer configured Client Id. This make sure an Access Token for another application is not being inappropriately reused in GeoServer (cf. AudienceAccessTokenValidator.java).

  3. The Subject of the userinfo and Access Token are verified to be about the same person. The OpenID specification recommends checking this (cf. SubjectTokenValidator.java).

For KeyCloak, consider using the “userinfo endpoint” role source and configure Keycloak to put groups in the “userinfo.”

For Azure AD, configure Azure to allow access to the MS Graph API (memberOf) and use the “Microsoft Graph API (Azure AD)” role source.

To configure Azure AD for “memberOf” (“GroupMember.Read.All” permission) access;

  1. go to your application in Azure AD (in the portal)

  2. On the left, go to “API permissions”

  3. click “Add a permission”

  4. press “Microsoft Graph”

  5. press “Delegated permission”

  6. Scroll down to “GroupMember”

  7. Choose “GroupMemeber.Read.All”

  8. press “Add permission”

  9. on the API Permission screen, press the “Grant admin consent for …” text

This has been tested with KeyCloak (with groups in the userinfo endpoint response), and with MS Azure AD (with the groups from the GraphAPI). This should work with other IDPs - however, make sure that the Subject and Audience token verification works with their tokens.

If you do not need Bearer Token functionality, it is recommended to turn this off.

Azure AD and ADFS setup

To make the OpenIdConnect filter to work properly with an Azure AD or ADFS server via the OpenId protocol, the user must set, in addition to the other configuration parameters, the Response Mode to query (otherwise by default ADFS will return a url fragment) and check the checkbox Send Client Secret in Token Request (the client_secret is mandatory in token request according to the Microsoft documentation).

../../_images/adfs-setup.png

SSL Trusted Certificates

When using a custom Keystore or trying to access a non-trusted or self-signed SSL-protected OAuth2 Provider from a non-SSH connection, you will need to add the certificates to the JVM Keystore.

In order to do this you can follow the next steps:

In this example we are going to

  1. Retrieve SSL certificates from Google domains:

    “Access Token URI” = https://accounts.google.com/o/oauth2/token therefore we need to trust https://accounts.google.com or (accounts.google.com:443) “Check Token Endpoint URL” = https://www.googleapis.com/oauth2/v1/tokeninfo therefore we need to trust https://www.googleapis.com or (www.googleapis.com:443)

    Note

    You will need to get and trust certificates from every different HTTPS URL used on OAuth2 Endpoints.

  2. Store SSL Certificates on local hard disk

  3. Add SSL Certificates to the Java Keystore

  4. Enable the JVM to check for SSL Certificates from the Keystore

  1. Retrieve the SSL Certificates from Google domains

    Use the openssl command in order to dump the certificate

    For https://accounts.google.com

    openssl s_client -connect accounts.google.com:443
    
    ../../_images/google_ssl_001.png

    And for https://www.googleapis.com

    openssl s_client -connect www.googleapis.com:443
    
    ../../_images/google_ssl_002.png
  2. Store SSL Certificates on local hard disk

    Copy-and-paste the two sections -BEGIN CERTIFICATE-, -END CERTIFICATE- and save them into two different .cert files

    Note

    .cert file are plain text files containing the ASCII characters included on the -BEGIN CERTIFICATE-, -END CERTIFICATE- sections

    google.cert (or whatever name you want with .cert extension)

    ../../_images/google_ssl_003.png

    google-apis.cert (or whatever name you want with .cert extension)

    ../../_images/google_ssl_004.png
  3. Add SSL Certificates to the Java Keystore

    You can use the Java command keytool like this

    google.cert (or whatever name you want with .cert extension)

    keytool -import -noprompt -trustcacerts -alias google -file google.cert -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS}
    

    google-apis.cert (or whatever name you want with .cert extension)

    keytool -import -noprompt -trustcacerts -alias google-apis -file google-apis.cert -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS}
    

    or, alternatively, you can use some graphic tool which helps you managing the SSL Certificates and Keystores, like Portecle

    java -jar c:\apps\portecle-1.9\portecle.jar
    
    ../../_images/google_ssl_005.png
    ../../_images/google_ssl_006.png
    ../../_images/google_ssl_007.png
    ../../_images/google_ssl_008.png
    ../../_images/google_ssl_009.png
    ../../_images/google_ssl_010.png
    ../../_images/google_ssl_011.png
    ../../_images/google_ssl_012.png
    ../../_images/google_ssl_013.png
  4. Enable the JVM to check for SSL Certificates from the Keystore

    In order to do this, you need to pass a JAVA_OPTION to your JVM:

    -Djavax.net.ssl.trustStore=F:\tmp\keystore.key
    
  5. Restart your server

Note

Here below you can find a bash script which simplifies the Keystore SSL Certificates importing. Use it at your convenience.

HOST=myhost.example.com
PORT=443
KEYSTOREFILE=dest_keystore
KEYSTOREPASS=changeme

# get the SSL certificate
openssl s_client -connect ${HOST}:${PORT} </dev/null \
        | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ${HOST}.cert

# create a keystore and import certificate
keytool -import -noprompt -trustcacerts \
        -alias ${HOST} -file ${HOST}.cert \
        -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS}

# verify we've got it.
keytool -list -v -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS} -alias ${HOST}