Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for Jetty OpenID Connect support #12559

Open
wants to merge 10 commits into
base: jetty-12.0.x
Choose a base branch
from
4 changes: 4 additions & 0 deletions documentation/jetty/modules/code/examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-openid</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-rewrite</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.docs.programming.security;

import java.util.Map;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.security.UserStore;
import org.eclipse.jetty.security.openid.OpenIdAuthenticator;
import org.eclipse.jetty.security.openid.OpenIdConfiguration;
import org.eclipse.jetty.security.openid.OpenIdLoginService;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.security.Credential;

public class OpenIdDocs
{
private static final String ISSUER = "";
private static final String CLIENT_ID = "";
private static final String CLIENT_SECRET = "";
private static final String TOKEN_ENDPOINT = "";
private static final String AUTH_ENDPOINT = "";
private static final String END_SESSION_ENDPOINT = "";
private static final String AUTH_METHOD = "";
private static final HttpClient httpClient = new HttpClient();

private OpenIdConfiguration openIdConfig;
private SecurityHandler securityHandler;

public void createConfigurationWithDiscovery()
{
// tag::createConfigurationWithDiscovery[]
OpenIdConfiguration openIdConfig = new OpenIdConfiguration(ISSUER, CLIENT_ID, CLIENT_SECRET);
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved
// end::createConfigurationWithDiscovery[]
}

public void createConfiguration()
{
// tag::createConfiguration[]
OpenIdConfiguration openIdConfig = new OpenIdConfiguration(ISSUER, TOKEN_ENDPOINT, AUTH_ENDPOINT, END_SESSION_ENDPOINT,
CLIENT_ID, CLIENT_SECRET, AUTH_METHOD, httpClient);
// end::createConfiguration[]
}

public void configureLoginService()
{
// tag::configureLoginService[]
LoginService loginService = new OpenIdLoginService(openIdConfig);
securityHandler.setLoginService(loginService);
// end::configureLoginService[]
}

public void configureAuthenticator()
{
// tag::configureAuthenticator[]
Authenticator authenticator = new OpenIdAuthenticator(openIdConfig, "/error");
securityHandler.setAuthenticator(authenticator);
// end::configureAuthenticator[]
}
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved

@SuppressWarnings("unchecked")
public void accessClaims()
{
Request request = new Request.Wrapper(null);

// tag::accessClaims[]
Map<String, Object> claims = (Map<String, Object>)request.getSession(true).getAttribute("org.eclipse.jetty.security.openid.claims");
String userId = (String)claims.get("sub");

Map<String, Object> response = (Map<String, Object>)request.getSession(true).getAttribute("org.eclipse.jetty.security.openid.response");
String accessToken = (String)response.get("access_token");
// tag::accessClaims[]
}

public void wrappedLoginService()
{
// tag::wrappedLoginService[]
// Use the optional LoginService for Roles.
LoginService wrappedLoginService = createWrappedLoginService();
LoginService loginService = new OpenIdLoginService(openIdConfig, wrappedLoginService);
// end::wrappedLoginService[]
}

private LoginService createWrappedLoginService()
{
HashLoginService loginService = new HashLoginService();
UserStore userStore = new UserStore();
userStore.addUser("admin", Credential.getCredential("password"), new String[]{"admin"});
loginService.setUserStore(userStore);
loginService.setName(ISSUER);
return loginService;
}
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 4 additions & 2 deletions documentation/jetty/modules/operations-guide/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
* xref:jstl/index.adoc[]
* xref:jsf-taglibs/index.adoc[]
* xref:jndi/index.adoc[]
* xref:jaas/index.adoc[]
* xref:jaspi/index.adoc[]
* Jetty Security
** xref:security/jaas-support.adoc[]
** xref:security/jaspi-support.adoc[]
** xref:security/openid-support.adoc[]
* xref:jmx/index.adoc[]
* xref:tools/index.adoc[]
* xref:troubleshooting/index.adoc[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,21 @@ However, we the RMI server is configured to bind to `localhost`, i.e. `127.0.0.1

If the system property `java.rmi.server.hostname` is not specified, the RMI client will try to connect to `127.0.1.1` (because that's what in the RMI stub) and fail because nothing is listening on that address.

[[openid]]
== Module `openid`

The `openid` module enables support for OpenID Connect (OIDC) authentication in Jetty, as detailed in xref:security/openid-support.adoc#openid-support[this section].

This module allows Jetty to authenticate users via an OpenID Connect identity provider, making possible to integrate features like "Sign in with Google" or "Sign in with Microsoft", among others.

This simplifies user authentication while leveraging the security and convenience of external identity providers.

The module properties are:

----
include::{jetty-home}/modules/openid.mod[tags=documentation]
----

[[qos]]
== Module `qos`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

[[openid-support]]
= OpenID Support

A more general discussion about OpenID and its support in Jetty is available in xref:programming-guide:security/openid-support.adoc[this section].

== OpenID Provider Configuration

To enable OpenID support, you need to enable the `openid` module:

----
$ java -jar $JETTY_HOME/start.jar --add-modules=openid
----

To configure OpenID Authentication with Jetty you will need to specify the OpenID Provider's issuer identifier (case-sensitive URL using the `https` scheme) and the OAuth 2.0 Client ID and Client Secret.

If the OpenID Provider does not allow metadata discovery you will also need to specify the token endpoint and authorization endpoint of the OpenID Provider.
These values can be set as properties in `$JETTY_BASE/start.d/openid.ini` file.

== Web Application Specific Configuration in `web.xml`

The `web.xml` file needs some specific configuration to use OpenID.

There must be a `login-config` element with an `auth-method` value of `OPENID`, and a `realm-name` value of the exact URL string used to set the OpenID Provider.

To set the error page, you must set an `init-param` named `org.eclipse.jetty.security.openid.error_page` whose value should be a path relative to the webapp where authentication errors should be redirected.

For example:

[,xml,subs=attributes+]
----
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
...
<login-config>
<auth-method>OPENID</auth-method>
<realm-name>https://accounts.google.com</realm-name>
</login-config>

<context-param>
<param-name>org.eclipse.jetty.security.openid.error_page</param-name>
<param-value>/error</param-value>
</context-param>
...
</web-app>
----

== Supporting Multiple OpenID Providers

You may override the `jetty-openid.xml` file in `$JETTY_BASE/etc/jetty-openid.xml` to add additional `OpenIdConfiguration` instances as beans on the server.

If there are multiple OpenID configuration instances found on the server then the `OpenIdAuthenticationFactory` will select the one with an `issuer` matching the `<realm-name>` of the `web.xml` for a given web app.
2 changes: 2 additions & 0 deletions documentation/jetty/modules/programming-guide/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
** xref:troubleshooting/state-tracking.adoc[]
** xref:troubleshooting/component-dump.adoc[]
** xref:troubleshooting/debugging.adoc[]
* Jetty Security
** xref:security/openid-support.adoc[]
* Migration Guides
** xref:migration/94-to-10.adoc[]
** xref:migration/11-to-12.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

[[openid-support]]
= OpenID Support

== External Setup
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved

=== Registering an App with OpenID Provider
You must register the app with an OpenID Provider such as link:https://developers.google.com/identity/protocols/OpenIDConnect[Google] or link:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html[Amazon].
This will give you a Client ID and Client Secret.

Once obtained the Client ID and the Client Secret, you must also register all the possible URIs for your web application with the path `/j_security_check` so that the OpenId Provider will allow redirection back to the webapp.

These may look like:

* `+http://localhost:8080/openid-webapp/j_security_check+`
* `+https://example.com/j_security_check+`

== Code Setup

=== Define the `OpenIdConfiguration` for a specific OpenID Provider.

If the OpenID Provider allows metadata discovery then you can use:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/security/OpenIdDocs.java[tags=createConfigurationWithDiscovery]
----

Otherwise, you can manually enter the necessary information:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/security/OpenIdDocs.java[tags=createConfiguration]
----

=== Configuring an `OpenIdLoginService`
[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/security/OpenIdDocs.java[tags=configureLoginService]
----

=== Configuring an `OpenIdAuthenticator` with `OpenIdConfiguration` and Error Page Redirect
[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/security/OpenIdDocs.java[tags=configureAuthenticator]
----

=== Usage

==== Claims and Access Token
Claims about the user can be found using attributes in the HTTP session attribute `org.eclipse.jetty.security.openid.claims`, and the full response containing the OAuth 2.0 Access Token can be found in the HTTP session attribute `org.eclipse.jetty.security.openid.response`.

Example:
[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/security/OpenIdDocs.java[tags=configureAuthenticator]
----

== Scopes
The OpenID scope is always used but additional scopes can be requested which can give you additional resources or privileges.

For the Google OpenID Provider it can be useful to request the scopes `profile` and `email` which will give you additional user claims.

Additional scopes can be specified using `OpenIdConfiguration.addScopes(\...);`.

== Authorization

If security roles are required they can be configured through a wrapped `LoginService` which is deferred to for role information by the `OpenIdLoginService`.

This can be configured in XML through `etc/openid-baseloginservice.xml` in the Distribution, or in embedded code using the constructor for the `OpenIdLoginService`.
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/security/OpenIdDocs.java[tags=wrappedLoginService]
----

When using authorization roles, the setting `authenticateNewUsers` becomes significant.
lachlan-roberts marked this conversation as resolved.
Show resolved Hide resolved
If set to `true` users not found by the wrapped `LoginService` will still be authenticated but will have no roles.
If set to `false` those users will be not be allowed to authenticate and are redirected to the error page.
This setting is configured through the property `jetty.openid.authenticateNewUsers` in the `start.ini` or `start.d/openid.ini` file, or with `OpenIdLoginService.setAuthenticateNewUsers(...);` in embedded code.
2 changes: 2 additions & 0 deletions jetty-core/jetty-openid/src/main/config/modules/openid.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ etc/jetty-openid-baseloginservice.xml
etc/jetty-openid.xml

[ini-template]
# tag::documentation[]
## The OpenID Identity Provider's issuer ID (the entire URL *before* ".well-known/openid-configuration")
# jetty.openid.provider=https://id.example.com/

Expand Down Expand Up @@ -48,3 +49,4 @@ etc/jetty-openid.xml

## Whether the user should be logged out after the idToken expires.
# jetty.openid.logoutWhenIdTokenIsExpired=false
# end::documentation[]
Loading