com.nimbusds.openid.connect.sdk.LogoutRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oauth2-oidc-sdk Show documentation
Show all versions of oauth2-oidc-sdk Show documentation
OAuth 2.0 SDK with OpenID Connection extensions for developing client
and server applications.
/*
* oauth2-oidc-sdk
*
* Copyright 2012-2016, Connect2id Ltd and contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.nimbusds.openid.connect.sdk;
import com.nimbusds.common.contenttype.ContentType;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.langtag.LangTag;
import com.nimbusds.langtag.LangTagException;
import com.nimbusds.langtag.LangTagUtils;
import com.nimbusds.oauth2.sdk.AbstractRequest;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.SerializeException;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
import com.nimbusds.oauth2.sdk.util.StringUtils;
import com.nimbusds.oauth2.sdk.util.URIUtils;
import com.nimbusds.oauth2.sdk.util.URLUtils;
import net.jcip.annotations.Immutable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
/**
* Logout request initiated by an OpenID relying party (RP). Supports HTTP GET
* and POST. HTTP POST is the recommended method to protect the optional ID
* token hint parameter from potentially getting recorded in access logs.
*
* Example HTTP POST request:
*
*
* POST /op/logout HTTP/1.1
* Host: server.example.com
* Content-Type: application/x-www-form-urlencoded
*
* id_token_hint=eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient.example.org%2Fpost-logout
* &state=af0ifjsldkj
*
*
* Example URL for an HTTP GET request:
*
*
* https://server.example.com/op/logout?
* id_token_hint=eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient.example.org%2Fpost-logout
* &state=af0ifjsldkj
*
*
* Related specifications:
*
*
* - OpenID Connect RP-Initiated Logout 1.0, section 2.
*
*/
@Immutable
public class LogoutRequest extends AbstractRequest {
/**
* The ID token hint (recommended).
*/
private final JWT idTokenHint;
/**
* The logout hint (optional).
*/
private final String logoutHint;
/**
* The client ID (optional).
*/
private final ClientID clientID;
/**
* The post-logout redirection URI (optional).
*/
private final URI postLogoutRedirectURI;
/**
* The state parameter (optional).
*/
private final State state;
/**
* The UI locales (optional).
*/
private final List uiLocales;
/**
* Creates a new OpenID Connect logout request.
*
* @param uri The URI of the end-session endpoint.
* May be {@code null} if the
* {@link #toHTTPRequest} method will not
* be used.
* @param idTokenHint The ID token hint (recommended),
* {@code null} if not specified.
* @param logoutHint The optional logout hint, {@code null}
* if not specified.
* @param clientID The optional client ID, {@code null} if
* not specified.
* @param postLogoutRedirectURI The optional post-logout redirection
* URI, {@code null} if not specified.
* @param state The optional state parameter for the
* post-logout redirection URI,
* {@code null} if not specified.
* @param uiLocales The optional end-user's preferred
* languages and scripts for the user
* interface, ordered by preference.
*/
public LogoutRequest(final URI uri,
final JWT idTokenHint,
final String logoutHint,
final ClientID clientID,
final URI postLogoutRedirectURI,
final State state,
final List uiLocales) {
super(uri);
this.idTokenHint = idTokenHint;
this.logoutHint = logoutHint;
this.clientID = clientID;
this.postLogoutRedirectURI = postLogoutRedirectURI;
if (postLogoutRedirectURI == null && state != null) {
throw new IllegalArgumentException("The state parameter requires a post-logout redirection URI");
}
this.state = state;
this.uiLocales = uiLocales;
}
/**
* Creates a new OpenID Connect logout request.
*
* @param uri The URI of the end-session endpoint.
* May be {@code null} if the
* {@link #toHTTPRequest} method will not
* be used.
* @param idTokenHint The ID token hint (recommended),
* {@code null} if not specified.
* @param postLogoutRedirectURI The optional post-logout redirection
* URI, {@code null} if not specified.
* @param state The optional state parameter for the
* post-logout redirection URI,
* {@code null} if not specified.
*/
public LogoutRequest(final URI uri,
final JWT idTokenHint,
final URI postLogoutRedirectURI,
final State state) {
this(uri, idTokenHint, null, null, postLogoutRedirectURI, state, null);
}
/**
* Creates a new OpenID Connect logout request without a post-logout
* redirection.
*
* @param uri The URI of the end-session endpoint. May be
* {@code null} if the {@link #toHTTPRequest} method
* will not be used.
* @param idTokenHint The ID token hint (recommended), {@code null} if
* not specified.
*/
public LogoutRequest(final URI uri,
final JWT idTokenHint) {
this(uri, idTokenHint, null, null);
}
/**
* Creates a new OpenID Connect logout request without a post-logout
* redirection.
*
* @param uri The URI of the end-session endpoint. May be {@code null}
* if the {@link #toHTTPRequest} method will not be used.
*/
public LogoutRequest(final URI uri) {
this(uri, null, null, null);
}
/**
* Returns the ID token hint. Corresponds to the optional
* {@code id_token_hint} parameter.
*
* @return The ID token hint, {@code null} if not specified.
*/
public JWT getIDTokenHint() {
return idTokenHint;
}
/**
* Returns the logout hint. Corresponds to the optional
* {@code logout_hint} parameter.
*
* @return The logout hint, {@code null} if not specified.
*/
public String getLogoutHint() {
return logoutHint;
}
/**
* Returns the client ID. Corresponds to the optional {@code client_id}
* parameter.
*
* @return The client ID, {@code null} if not specified.
*/
public ClientID getClientID() {
return clientID;
}
/**
* Return the post-logout redirection URI.
*
* @return The post-logout redirection URI, {@code null} if not
* specified.
*/
public URI getPostLogoutRedirectionURI() {
return postLogoutRedirectURI;
}
/**
* Returns the state parameter for a post-logout redirection URI.
* Corresponds to the optional {@code state} parameter.
*
* @return The state parameter, {@code null} if not specified.
*/
public State getState() {
return state;
}
/**
* Returns the end-user's preferred languages and scripts for the user
* interface, ordered by preference. Corresponds to the optional
* {@code ui_locales} parameter.
*
* @return The preferred UI locales, {@code null} if not specified.
*/
public List getUILocales() {
return uiLocales;
}
/**
* Returns the parameters for this logout request.
*
* Example parameters:
*
*
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* post_logout_redirect_uri = https://client.example.com/post-logout
* state = af0ifjsldkj
*
*
* @return The parameters.
*/
public Map> toParameters() {
Map > params = new LinkedHashMap<>();
if (getIDTokenHint() != null) {
try {
params.put("id_token_hint", Collections.singletonList(getIDTokenHint().serialize()));
} catch (IllegalStateException e) {
throw new SerializeException("Couldn't serialize ID token: " + e.getMessage(), e);
}
}
if (getLogoutHint() != null) {
params.put("logout_hint", Collections.singletonList(getLogoutHint()));
}
if (getClientID() != null) {
params.put("client_id", Collections.singletonList(getClientID().getValue()));
}
if (getPostLogoutRedirectionURI() != null) {
params.put("post_logout_redirect_uri", Collections.singletonList(getPostLogoutRedirectionURI().toString()));
}
if (getState() != null) {
params.put("state", Collections.singletonList(getState().getValue()));
}
if (getUILocales() != null) {
params.put("ui_locales", Collections.singletonList(LangTagUtils.concat(getUILocales())));
}
return params;
}
/**
* Returns the URI query string for this logout request.
*
* Note that the '?' character preceding the query string in a URI
* is not included in the returned string.
*
*
Example URI query string:
*
*
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fpost-logout
* &state=af0ifjsldkj
*
*
* @return The URI query string.
*/
public String toQueryString() {
return URLUtils.serializeParameters(toParameters());
}
/**
* Returns the complete URI representation for this logout request,
* consisting of the {@link #getEndpointURI end-session endpoint URI}
* with the {@link #toQueryString query string} appended.
*
* Example URI:
*
*
* https://server.example.com/logout?
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fpost-logout
* &state=af0ifjsldkj
*
*
* @return The URI representation.
*/
public URI toURI() {
if (getEndpointURI() == null)
throw new SerializeException("The end-session endpoint URI is not specified");
final Map> mergedQueryParams = new HashMap<>(URLUtils.parseParameters(getEndpointURI().getQuery()));
mergedQueryParams.putAll(toParameters());
String query = URLUtils.serializeParameters(mergedQueryParams);
if (StringUtils.isNotBlank(query)) {
query = '?' + query;
}
try {
return new URI(URIUtils.getBaseURI(getEndpointURI()) + query);
} catch (URISyntaxException e) {
throw new SerializeException(e.getMessage(), e);
}
}
@Override
public HTTPRequest toHTTPRequest() {
if (getEndpointURI() == null)
throw new SerializeException("The endpoint URI is not specified");
Map> mergedQueryParams = new LinkedHashMap<>(URLUtils.parseParameters(getEndpointURI().getQuery()));
mergedQueryParams.putAll(toParameters());
URL baseURL;
try {
baseURL = URLUtils.getBaseURL(getEndpointURI().toURL());
} catch (MalformedURLException e) {
throw new SerializeException(e.getMessage(), e);
}
HTTPRequest httpRequest;
httpRequest = new HTTPRequest(HTTPRequest.Method.POST, baseURL);
httpRequest.setEntityContentType(ContentType.APPLICATION_URLENCODED);
httpRequest.setBody(URLUtils.serializeParameters(mergedQueryParams));
return httpRequest;
}
/**
* Parses a logout request from the specified parameters.
*
* Example parameters:
*
*
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* post_logout_redirect_uri = https://client.example.com/post-logout
* state = af0ifjsldkj
*
*
* @param params The parameters, empty map if none. Must not be
* {@code null}.
*
* @return The logout request.
*
* @throws ParseException If the parameters couldn't be parsed to a
* logout request.
*/
public static LogoutRequest parse(final Map> params)
throws ParseException {
return parse(null, params);
}
/**
* Parses a logout request from the specified URI and query parameters.
*
* Example parameters:
*
*
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* post_logout_redirect_uri = https://client.example.com/post-logout
* state = af0ifjsldkj
*
*
* @param uri The URI of the end-session endpoint. May be
* {@code null} if the {@link #toHTTPRequest()} method
* will not be used.
* @param params The parameters, empty map if none. Must not be
* {@code null}.
*
* @return The logout request.
*
* @throws ParseException If the parameters couldn't be parsed to a
* logout request.
*/
public static LogoutRequest parse(final URI uri, final Map> params)
throws ParseException {
String v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint");
JWT idTokenHint = null;
if (StringUtils.isNotBlank(v)) {
try {
idTokenHint = JWTParser.parse(v);
} catch (java.text.ParseException e) {
throw new ParseException("Invalid id_token_hint: " + e.getMessage(), e);
}
}
String logoutHint = MultivaluedMapUtils.getFirstValue(params, "logout_hint");
ClientID clientID = null;
v = MultivaluedMapUtils.getFirstValue(params, "client_id");
if (StringUtils.isNotBlank(v)) {
clientID = new ClientID(v);
}
v = MultivaluedMapUtils.getFirstValue(params, "post_logout_redirect_uri");
URI postLogoutRedirectURI = null;
if (StringUtils.isNotBlank(v)) {
try {
postLogoutRedirectURI = new URI(v);
} catch (URISyntaxException e) {
throw new ParseException("Invalid post_logout_redirect_uri parameter: " + e.getMessage(), e);
}
}
State state = null;
v = MultivaluedMapUtils.getFirstValue(params, "state");
if (postLogoutRedirectURI != null && StringUtils.isNotBlank(v)) {
state = new State(v);
}
List uiLocales;
try {
uiLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "ui_locales"));
} catch (LangTagException e) {
throw new ParseException("Invalid ui_locales parameter: " + e.getMessage(), e);
}
return new LogoutRequest(uri, idTokenHint, logoutHint, clientID, postLogoutRedirectURI, state, uiLocales);
}
/**
* Parses a logout request from the specified URI query string.
*
* Example URI query string:
*
*
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fpost-logout
* &state=af0ifjsldkj
*
*
* @param query The URI query string, {@code null} if none.
*
* @return The logout request.
*
* @throws ParseException If the query string couldn't be parsed to a
* logout request.
*/
public static LogoutRequest parse(final String query)
throws ParseException {
return parse(null, URLUtils.parseParameters(query));
}
/**
* Parses a logout request from the specified URI query string.
*
* Example URI query string:
*
*
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fpost-logout
* &state=af0ifjsldkj
*
*
* @param uri The URI of the end-session endpoint. May be
* {@code null} if the {@link #toHTTPRequest()} method
* will not be used.
* @param query The URI query string, {@code null} if none.
*
* @return The logout request.
*
* @throws ParseException If the query string couldn't be parsed to a
* logout request.
*/
public static LogoutRequest parse(final URI uri, final String query)
throws ParseException {
return parse(uri, URLUtils.parseParameters(query));
}
/**
* Parses a logout request from the specified URI.
*
* Example URI:
*
*
* https://server.example.com/logout?
* id_token_hint = eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fpost-logout
* &state=af0ifjsldkj
*
*
* @param uri The URI. Must not be {@code null}.
*
* @return The logout request.
*
* @throws ParseException If the URI couldn't be parsed to a logout
* request.
*/
public static LogoutRequest parse(final URI uri)
throws ParseException {
return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
}
/**
* Parses a logout request from the specified HTTP GET or POST request.
*
* Example HTTP POST request:
*
*
* POST /op/logout HTTP/1.1
* Host: server.example.com
* Content-Type: application/x-www-form-urlencoded
*
* id_token_hint=eyJhbGciOiJSUzI1NiJ9.eyJpc3Mi...
* &post_logout_redirect_uri=https%3A%2F%2Fclient.example.org%2Fpost-logout
* &state=af0ifjsldkj
*
*
* @param httpRequest The HTTP request. Must not be {@code null}.
*
* @return The logout request.
*
* @throws ParseException If the HTTP request couldn't be parsed to a
* logout request.
*/
public static LogoutRequest parse(final HTTPRequest httpRequest)
throws ParseException {
if (HTTPRequest.Method.POST.equals(httpRequest.getMethod())) {
httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
return LogoutRequest.parse(httpRequest.getURI(), httpRequest.getBodyAsFormParameters());
}
if (HTTPRequest.Method.GET.equals(httpRequest.getMethod())) {
return LogoutRequest.parse(httpRequest.getURI());
}
throw new ParseException("The HTTP request method must be POST or GET");
}
}