All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.security.openid.OpenIdCredentials Maven / Gradle / Ivy

There is a newer version: 12.1.0.alpha0
Show newest version
//
// ========================================================================
// Copyright (c) 1995-2021 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.security.openid;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.FormRequestContent;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.ajax.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

The credentials of an user to be authenticated with OpenID Connect. This will contain * the OpenID ID Token and the OAuth 2.0 Access Token.

* *

* This is constructed with an authorization code from the authentication request. This authorization code * is then exchanged using {@link #redeemAuthCode(OpenIdConfiguration)} for a response containing the ID Token and Access Token. * The response is then validated against the {@link OpenIdConfiguration}. *

*/ public class OpenIdCredentials implements Serializable { private static final Logger LOG = LoggerFactory.getLogger(OpenIdCredentials.class); private static final long serialVersionUID = 4766053233370044796L; private final String redirectUri; private String authCode; private Map response; private Map claims; public OpenIdCredentials(String authCode, String redirectUri) { this.authCode = authCode; this.redirectUri = redirectUri; } public String getUserId() { return (String)claims.get("sub"); } public Map getClaims() { return claims; } public Map getResponse() { return response; } public void redeemAuthCode(OpenIdConfiguration configuration) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("redeemAuthCode() {}", this); if (authCode != null) { try { response = claimAuthCode(configuration); if (LOG.isDebugEnabled()) LOG.debug("response: {}", response); String idToken = (String)response.get("id_token"); if (idToken == null) throw new AuthenticationException("no id_token"); String accessToken = (String)response.get("access_token"); if (accessToken == null) throw new AuthenticationException("no access_token"); String tokenType = (String)response.get("token_type"); if (!"Bearer".equalsIgnoreCase(tokenType)) throw new AuthenticationException("invalid token_type"); claims = JwtDecoder.decode(idToken); if (LOG.isDebugEnabled()) LOG.debug("claims {}", claims); validateClaims(configuration); } finally { // reset authCode as it can only be used once authCode = null; } } } private void validateClaims(OpenIdConfiguration configuration) throws Exception { // Issuer Identifier for the OpenID Provider MUST exactly match the value of the iss (issuer) Claim. if (!configuration.getIssuer().equals(claims.get("iss"))) throw new AuthenticationException("Issuer Identifier MUST exactly match the iss Claim"); // The aud (audience) Claim MUST contain the client_id value. validateAudience(configuration); // If an azp (authorized party) Claim is present, verify that its client_id is the Claim Value. Object azp = claims.get("azp"); if (azp != null && !configuration.getClientId().equals(azp)) throw new AuthenticationException("Authorized party claim value should be the client_id"); // Check that the ID token has not expired by checking the exp claim. long expiry = (Long)claims.get("exp"); long currentTimeSeconds = (long)(System.currentTimeMillis() / 1000F); if (currentTimeSeconds > expiry) throw new AuthenticationException("ID Token has expired"); } private void validateAudience(OpenIdConfiguration configuration) throws AuthenticationException { Object aud = claims.get("aud"); String clientId = configuration.getClientId(); boolean isString = aud instanceof String; boolean isList = aud instanceof Object[]; boolean isValidType = isString || isList; if (isString && !clientId.equals(aud)) throw new AuthenticationException("Audience Claim MUST contain the client_id value"); else if (isList) { if (!Arrays.asList((Object[])aud).contains(clientId)) throw new AuthenticationException("Audience Claim MUST contain the client_id value"); if (claims.get("azp") == null) throw new AuthenticationException("A multi-audience ID token needs to contain an azp claim"); } else if (!isValidType) throw new AuthenticationException("Audience claim was not valid"); } @SuppressWarnings("unchecked") private Map claimAuthCode(OpenIdConfiguration configuration) throws Exception { Fields fields = new Fields(); fields.add("code", authCode); fields.add("client_id", configuration.getClientId()); fields.add("client_secret", configuration.getClientSecret()); fields.add("redirect_uri", redirectUri); fields.add("grant_type", "authorization_code"); FormRequestContent formContent = new FormRequestContent(fields); Request request = configuration.getHttpClient().POST(configuration.getTokenEndpoint()) .body(formContent) .timeout(10, TimeUnit.SECONDS); ContentResponse response = request.send(); String responseBody = response.getContentAsString(); if (LOG.isDebugEnabled()) LOG.debug("Authentication response: {}", responseBody); Object parsedResponse = new JSON().fromJSON(responseBody); if (!(parsedResponse instanceof Map)) throw new AuthenticationException("Malformed response from OpenID Provider"); return (Map)parsedResponse; } public static class AuthenticationException extends Exception { public AuthenticationException(String message) { super(message); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy