oidc.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. OpenID connect authentication
  2. -----------------------------
  3. The OAuth2 OpenID Connect (OIDC) authentication is working in a way quite similar to Google (and GitHub)
  4. authentications, the only difference is that the authentication page cannot propose default
  5. values for the various endpoints, which have to be configured manually.
  6. In case the web login will not be used, the ``client ID`` and ``client secret`` are not actually
  7. needed, and can be filled with two made up values (the validation just checks they are present,
  8. but they will be used only in the "authorisation flow", but not when doing OGC requests
  9. where the client is supposed to have autonomously retrieved a valid bearer token).
  10. The configuration GUI supports OpenID Discovery documents. If the server supports them
  11. it's sufficient to provide the path to the document, or to the authentication service root,
  12. and the GUI will auto-fill itself based on the document contents:
  13. .. figure:: images/discovery.png
  14. :align: center
  15. The UI allows 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.
  16. In addition, the OpenID connect authentication is able to extract the user roles from either the ID token or the Access Token:
  17. .. figure:: images/openidconnect-roles.png
  18. :align: center
  19. 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.
  20. From the 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 is used, as the Identity Provider doesn't send the authorization code by default as a query string (that is mandatory in order to allow GeoServer and OpenId integration to work properly).
  21. Finally, the admin can allow the sending of the ``client_secret`` during an access_token request through the ``Send Client Secret in Token Request``. Some OpenId implementations requires it for the Authorization Code flow when the client app is a confidential client and can safely store the client_secret.
  22. Logging OAuth2 Activity
  23. ^^^^^^^^^^^^^^^^^^^^^^^
  24. The plugin includes an ``OIDC_LOGGING`` profile which is installed on startup. This logging profile quiets most GeoServer logging activity, while enabling trace logging for OAuth2 functionality.
  25. The module also includes an additional connection setting to include the token details as additional log messages. This is intended to assist in troubleshooting during development and initial setup.
  26. .. figure:: images/log-sensitive-information.png
  27. Log sensitive information
  28. This setting can obviously be used to access sensitive information, and you are advised to clear logs after use.
  29. To setup for troubleshooting OIDC activity:
  30. #. Navigate to :menuselection:`Settings --> Global`
  31. #. Select the logging profile ``OIDC_LOGGING``
  32. #. Navigate :menuselection:`Security --> Authentication`
  33. #. Setup your OAuth2 OpenID Connect configuration with :guilabel:`Log Sensitive Information (do not use in production)` checked
  34. #. With these settings each individual step of the OAuth2 authentication is shown. The logging sensitive information setting logs access token and id token (the contents of these tokens may be decoded using https://jwt.io).
  35. .. code-block:: text
  36. DEBUG [security.oauth2] - OIDC: - CLIENT_SECRET: squirrel
  37. DEBUG [security.oauth2] - OIDC: received a CODE from Identity Provider - handing it in for ID/Access Token
  38. DEBUG [security.oauth2] - OIDC: CODE=...
  39. DEBUG [security.oauth2] - OIDC: Identity Provider returned Token, type=Bearer
  40. DEBUG [security.oauth2] - OIDC: SCOPES=openid geocat
  41. DEBUG [security.oauth2] - OIDC: ACCESS TOKEN: ....
  42. DEBUG [security.oauth2] - OIDC: ID TOKEN: ...
  43. DEBUG [security.oauth2] - OIDC: Getting Roles from UserGroupService, location=null
  44. DEBUG [security.oauth2] - OIDC: Geoserver Roles: ADMIN
  45. DEBUG [security.oauth2] - OIDC: Geoserver Roles: ROLE_ADMINISTRATOR
  46. OpenID Connect With Attached Access Bearer Tokens
  47. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  48. 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.
  49. .. figure:: images/bearer_tokens.png
  50. Bearer Tokens
  51. The setup process is as follows:
  52. #. Setup your OAuth2 OpenID Connect configuration as normal
  53. #. On the OpenID Connect configuration screen (bottom), makes sure "Allow Attached Bearer Tokens" is checked
  54. #. You cannot use ID Tokens as a Role Source for the attached Bearer Tokens (see below)
  55. To Use:
  56. #. Obtain an Access Token from the underlying IDP
  57. #. Attach the access token to your HTTP request headers
  58. `Authorization: Bearer <token>`
  59. The Access Token (JWT) is validated;
  60. #. The Access Token is used to get the "userinfo" endpoint. The underlying IDP will verify the token (i.e. signature and expiry)
  61. #. 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`).
  62. #. 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`).
  63. For KeyCloak, consider using the "userinfo endpoint" role source and configure Keycloak to put groups in the "userinfo."
  64. For Azure AD, configure Azure to allow access to the MS Graph API (memberOf) and use the "Microsoft Graph API (Azure AD)" role source.
  65. To configure Azure AD for "memberOf" ("GroupMember.Read.All" permission) access;
  66. #. Go to your application in Azure AD (in the portal)
  67. #. On the left, go to "API permissions"
  68. #. Click "Add a permission"
  69. #. press "Microsoft Graph"
  70. #. press "Delegated permission"
  71. #. Scroll down to "GroupMember"
  72. #. Choose "GroupMember.Read.All"
  73. #. Press "Add permission"
  74. #. On the API Permission screen, press the "Grant admin consent for ..." text
  75. 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.
  76. If you do not need Bearer Token functionality, it is recommended to turn this off.
  77. Proof Key of Code Exchange (PKCE)
  78. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  79. The OpenID Connect plugin allows the use of Proof Key of Code Exchange (PKCE).
  80. .. figure:: images/pkce.png
  81. Proof Key of Code Exchange
  82. The setup process is as follows:
  83. #. Setup your OAuth2 OpenID Connect configuration as normal
  84. #. On the OpenID Connect configuration screen (bottom), makes sure "Use PKCE" is checked
  85. To prevent client side request forgery:
  86. * **Step 1**: GeoServer will include a ``code_challenge`` during initial authorization code request
  87. * **Step 2**: GeoServer will include a ``code_verifer`` during the access token request.
  88. The authentication server will confirm that ``code_verifier`` hash matches the initial ``code_challenge``
  89. in order the confirm the client is the same as in **Step 1**.
  90. Log output of this exchange is as follows:
  91. .. code-block::
  92. DEBUG [oauth2.pkce] - Generate code_verifier: yQat4Y.....
  93. DEBUG [oauth2.pkce] - CODE_CHALLENGE: 5HiD...
  94. DEBUG [oauth2.pkce] - CODE_CHALLENGE_METHOD: S256
  95. DEBUG [oauth2.pkce] - CLIENT_SECRET: squirrel
  96. DEBUG [oauth2.pkce] - CODE_VERIFIER: yQat4Y...
  97. Reference:
  98. * `rfc7636 Proof Key for Code Exchange by OAuth Public Clients <https://datatracker.ietf.org/doc/html/rfc7636>`_
  99. JSON Web Key set URI
  100. ^^^^^^^^^^^^^^^^^^^^
  101. The ``JSON Web Key set URI`` provides the location of a document of public keys that can be used to check the signature of the provided accessToken.
  102. Optional: It is no longer required to use ``Check Token Endpoint URL`` - if you leave that field blank you may rely only on the ``JSON Web Key set URI`` signature check. When use in this manner roles cannot be extracted from access token.
  103. Enforce Token Validation
  104. ^^^^^^^^^^^^^^^^^^^^^^^^
  105. `True` by default.
  106. Check this option to enforce the validation of the token signature.
  107. Per the `RFC 7517` or this doc from `auth0`, the parameters does not include neither `public_key_use` (but use nor `key_id` (but `kid`)
  108. The RFC specifies that kid is optional (`RFC 7517: JSON Web Key (JWK)`) Use of this member is `OPTIONAL`.
  109. Reference:
  110. * `RFC 7517: JSON Web Key (JWK) <https://www.rfc-editor.org/rfc/rfc7517#section-4.5>`_
  111. Azure AD and ADFS setup
  112. ^^^^^^^^^^^^^^^^^^^^^^^
  113. 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 <https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-openid-connect-oauth-flows-scenarios#request-an-access-token>`_).
  114. .. figure:: images/adfs-setup.png
  115. :align: center
  116. SSL Trusted Certificates
  117. ------------------------
  118. 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``.
  119. In order to do this you can follow the next steps:
  120. In this example we are going to
  121. #. Retrieve SSL certificates from Google domains:
  122. "Access Token URI" = https://accounts.google.com/o/oauth2/token therefore we need to trust ``https://accounts.google.com`` or (``accounts.google.com:443``)
  123. "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``)
  124. .. note:: You will need to get and trust certificates from every different HTTPS URL used on OAuth2 Endpoints.
  125. #. Store SSL Certificates on local hard disk
  126. #. Add SSL Certificates to the Java Keystore
  127. #. Enable the JVM to check for SSL Certificates from the Keystore
  128. 1. Retrieve the SSL Certificates from Google domains
  129. Use the ``openssl`` command in order to dump the certificate
  130. For ``https://accounts.google.com``
  131. .. code-block:: shell
  132. openssl s_client -connect accounts.google.com:443
  133. .. figure:: images/google_ssl_001.png
  134. :align: center
  135. And for ``https://www.googleapis.com``
  136. .. code-block:: shell
  137. openssl s_client -connect www.googleapis.com:443
  138. .. figure:: images/google_ssl_002.png
  139. :align: center
  140. 2. Store SSL Certificates on local hard disk
  141. Copy-and-paste the two sections ``-BEGIN CERTIFICATE-``, ``-END CERTIFICATE-`` and save them into two different ``.cert`` files
  142. .. note:: ``.cert`` file are plain text files containing the ASCII characters included on the ``-BEGIN CERTIFICATE-``, ``-END CERTIFICATE-`` sections
  143. ``google.cert`` (or whatever name you want with ``.cert`` extension)
  144. .. figure:: images/google_ssl_003.png
  145. :align: center
  146. ``google-apis.cert`` (or whatever name you want with ``.cert`` extension)
  147. .. figure:: images/google_ssl_004.png
  148. :align: center
  149. 3. Add SSL Certificates to the Java Keystore
  150. You can use the Java command ``keytool`` like this
  151. ``google.cert`` (or whatever name you want with ``.cert`` extension)
  152. .. code-block:: shell
  153. keytool -import -noprompt -trustcacerts -alias google -file google.cert -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS}
  154. ``google-apis.cert`` (or whatever name you want with ``.cert`` extension)
  155. .. code-block:: shell
  156. keytool -import -noprompt -trustcacerts -alias google-apis -file google-apis.cert -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS}
  157. or, alternatively, you can use some graphic tool which helps you managing the SSL Certificates and Keystores, like `Portecle <http://portecle.sourceforge.net/>`_
  158. .. code-block:: shell
  159. java -jar c:\apps\portecle-1.9\portecle.jar
  160. .. figure:: images/google_ssl_005.png
  161. :align: center
  162. .. figure:: images/google_ssl_006.png
  163. :align: center
  164. .. figure:: images/google_ssl_007.png
  165. :align: center
  166. .. figure:: images/google_ssl_008.png
  167. :align: center
  168. .. figure:: images/google_ssl_009.png
  169. :align: center
  170. .. figure:: images/google_ssl_010.png
  171. :align: center
  172. .. figure:: images/google_ssl_011.png
  173. :align: center
  174. .. figure:: images/google_ssl_012.png
  175. :align: center
  176. .. figure:: images/google_ssl_013.png
  177. :align: center
  178. 4. Enable the JVM to check for SSL Certificates from the Keystore
  179. In order to do this, you need to pass a ``JAVA_OPTION`` to your JVM:
  180. .. code-block:: shell
  181. -Djavax.net.ssl.trustStore=F:\tmp\keystore.key
  182. 5. Restart your server
  183. .. note:: Here below you can find a bash script which simplifies the Keystore SSL Certificates importing. Use it at your convenience.
  184. .. code-block:: shell
  185. HOST=myhost.example.com
  186. PORT=443
  187. KEYSTOREFILE=dest_keystore
  188. KEYSTOREPASS=changeme
  189. # get the SSL certificate
  190. openssl s_client -connect ${HOST}:${PORT} </dev/null \
  191. | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ${HOST}.cert
  192. # create a keystore and import certificate
  193. keytool -import -noprompt -trustcacerts \
  194. -alias ${HOST} -file ${HOST}.cert \
  195. -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS}
  196. # verify we've got it.
  197. keytool -list -v -keystore ${KEYSTOREFILE} -storepass ${KEYSTOREPASS} -alias ${HOST}