diff --git a/doc/source/howtos/admin.rst b/doc/source/howtos/admin.rst index 9fd4933f27..c4301f97ea 100644 --- a/doc/source/howtos/admin.rst +++ b/doc/source/howtos/admin.rst @@ -6,5 +6,6 @@ Admin How-to Guides installation zuul-from-scratch + openid-connect-examples troubleshooting zookeeper diff --git a/doc/source/howtos/openid-connect-examples.rst b/doc/source/howtos/openid-connect-examples.rst new file mode 100644 index 0000000000..a4fedc43d3 --- /dev/null +++ b/doc/source/howtos/openid-connect-examples.rst @@ -0,0 +1,21 @@ +OpenID Connect Integration Examples +=================================== + +This document lists simple How-Tos to help administrators enable OpenID +Connect authentication in Zuul and Zuul's Web UI. + +.. toctree:: + :maxdepth: 1 + + openid-with-google + openid-with-keycloak + +Debugging +--------- + +If problems appear: + +* Make sure your configuration is correct, especially callback URIs. +* More information can be found in Zuul's web service logs. +* From the user's side, activating the web console in the browser can be helpful + to debug API calls. diff --git a/doc/source/howtos/openid-with-google.rst b/doc/source/howtos/openid-with-google.rst new file mode 100644 index 0000000000..4b25337ef3 --- /dev/null +++ b/doc/source/howtos/openid-with-google.rst @@ -0,0 +1,65 @@ +Configuring Google Authentication +================================= + +This document explains how to configure Zuul in order to enable authentication +with Google. + +Prerequisites +------------- + +* The Zuul instance must be able to query Google's OAUTH API servers. This + simply generally means that the Zuul instance must be able to send and + receive HTTPS data to and from the Internet. +* You must set up a project in `Google's developers console `_. + +Setting up credentials with Google +---------------------------------- + +In the developers console, choose your project and click `APIs & Services`. + +Choose `Credentials` in the menu on the left, then click `Create Credentials`. + +Choose `Create OAuth client ID`. You might need to configure a consent screen first. + +Create OAuth client ID +...................... + +Choose `Web application` as Application Type. + +In `Authorized JavaScript Origins`, add the base URL of Zuul's Web UI. For example, +if you are running a yarn development server on your computer, it would be +`http://localhost:3000` . + +In `Authorized redirect URIs`, write down the base URL of Zuul's Web UI followed +by "/t//auth_callback", for each tenant on which you want to enable +authentication. For example, if you are running a yarn development server on +your computer and want to set up authentication for tenant "local", +write `http://localhost:3000/t/local/auth_callback` . + +Click Save. Google will generate a Client ID and a Client secret for your new +credentials; we will only need the Client ID for the rest of this How-To. + +Configure Zuul +.............. + +Edit the ``/etc/zuul/zuul.conf`` to add the google authenticator: + +.. code-block:: ini + + [auth google_auth] + default=true + driver=OpenIDConnect + realm=my_realm + issuer_id=https://accounts.google.com + client_id= + + +Restart Zuul services (scheduler, web). + +Head to your tenant's status page. If all went well, you should see a "Sign in" +button in the upper right corner of the page. Congratulations! + +Further Reading +--------------- + +This How-To is based on `Google's documentation on their implementation of OpenID Connect `_. diff --git a/doc/source/howtos/openid-with-keycloak.rst b/doc/source/howtos/openid-with-keycloak.rst new file mode 100644 index 0000000000..e803768010 --- /dev/null +++ b/doc/source/howtos/openid-with-keycloak.rst @@ -0,0 +1,110 @@ +Configuring Keycloak Authentication +=================================== + +This document explains how to configure Zuul and Keycloak in order to enable +authentication in Zuul with Keycloak. + +Prerequisites +------------- + +* The Zuul instance must be able to query Keycloak over HTTPS. +* Authenticating users must be able to reach Keycloak's web UI. +* Have a realm set up in Keycloak. + `Instructions on how to do so can be found here `_ . + +By convention, we will assume the Keycloak server's FQDN is ``keycloak``, and +Zuul's Web UI's base URL is ``https://zuul/``. We will use the realm ``my_realm``. + +Most operations below regarding the configuration of Keycloak can be performed through +Keycloak's admin CLI. The following steps must be performed as an admin on Keycloak's +GUI. + +Setting up Keycloak +------------------- + +Create a client +............... + +Choose the realm ``my_realm``, then click ``clients`` in the Configure panel. +Click ``Create``. + +Name your client as you please. We will pick ``zuul`` for this example. Make sure +to fill the following fields: + +* Client Protocol: ``openid-connect`` +* Access Type: ``public`` +* Implicit Flow Enabled: ``ON`` +* Valid Redirect URIs: ``https://zuul/*`` +* Web Origins: ``https://zuul/`` + +Click "Save" when done. + +Create a client scope +...................... + +Keycloak maps the client ID to a specific claim, instead of the usual `aud` claim. +We need to configure Keycloak to add our client ID to the `aud` claim by creating +a custom client scope for our client. + +Choose the realm ``my_realm``, then click ``client scopes`` in the Configure panel. +Click ``Create``. + +Name your scope as you please. We will name it ``zuul_aud`` for this example. +Make sure you fill the following fields: + +* Protocol: ``openid-connect`` +* Include in Token Scope: ``ON`` + +Click "Save" when done. + +On the Client Scopes page, click on ``zuul_aud`` to configure it; click on +``Mappers`` then ``create``. + +Make sure to fill the following: + +* Mapper Type: ``Audience`` +* Included Client Audience: ``zuul`` +* Add to ID token: ``ON`` +* Add to access token: ``ON`` + +Then save. + +Finally, go back to the clients list and pick the ``zuul`` client again. Click +on ``Client Scopes``, and add the ``zuul_aud`` scope to the ``Assigned Default +Client Scopes``. + +(Optional) Set up a social identity provider +............................................ + +Keycloak can delegate authentication to predefined social networks. Follow +`these steps to find out how. `_ + +If you don't set up authentication delegation, make sure to create at least one +user in your realm, or allow self-registration. See Keycloak's documentation section +on `user management `_ +for more details on how to do so. + +Setting up Zuul +--------------- + +Edit the ``/etc/zuul/zuul.conf`` to add the keycloak authenticator: + +.. code-block:: ini + + [auth keycloak] + default=true + driver=OpenIDConnect + realm=my_realm + issuer_id=https://keycloak/auth/realms/my_realm + client_id=zuul + +Restart Zuul services (scheduler, web). + +Head to your tenant's status page. If all went well, you should see a "Sign in" +button in the upper right corner of the page. Congratulations! + +Further Reading +--------------- + +This How-To is based on `Keycloak's documentation `_, +specifically `the documentation about clients `_. diff --git a/releasenotes/notes/webui-openidconnect-514c09b26f7fd15e.yaml b/releasenotes/notes/webui-openidconnect-514c09b26f7fd15e.yaml new file mode 100644 index 0000000000..030eacf452 --- /dev/null +++ b/releasenotes/notes/webui-openidconnect-514c09b26f7fd15e.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add authentication in the web UI. Zuul's web UI can be configured to authenticate + users against an Identity Provider supporting the OpenID Connect protocol. diff --git a/web/package.json b/web/package.json index e13a7d9c3b..16c3e88c14 100644 --- a/web/package.json +++ b/web/package.json @@ -17,6 +17,8 @@ "moment": "^2.22.2", "moment-duration-format": "2.3.2", "moment-timezone": "^0.5.28", + "oidc-client": "^1.10.1", + "oidc-react": "^1.5.1", "patternfly-react": "^2.39.16", "prop-types": "^15.6.2", "react": "^16.13.1", diff --git a/web/src/App.jsx b/web/src/App.jsx index c64f09ccf2..68e7ca3790 100644 --- a/web/src/App.jsx +++ b/web/src/App.jsx @@ -58,6 +58,7 @@ import { UsersIcon, } from '@patternfly/react-icons' +import AuthContainer from './containers/auth/Auth' import ErrorBoundary from './containers/ErrorBoundary' import { Fetching } from './containers/Fetching' import SelectTz from './containers/timezone/SelectTz' @@ -67,6 +68,7 @@ import { clearError } from './actions/errors' import { fetchConfigErrorsAction } from './actions/configErrors' import { routes } from './routes' import { setTenantAction } from './actions/tenant' +import { configureAuthFromTenant, configureAuthFromInfo } from './actions/auth' class App extends React.Component { static propTypes = { @@ -79,6 +81,7 @@ class App extends React.Component { history: PropTypes.object, dispatch: PropTypes.func, isKebabDropdownOpen: PropTypes.bool, + user: PropTypes.object, } state = { @@ -106,7 +109,7 @@ class App extends React.Component { ) } else { // Return an empty navigation bar in case we don't have an active tenant - return