com.authlete.jakarta.AuthorizationDecisionHandler Maven / Gradle / Ivy
Show all versions of authlete-java-jakarta Show documentation
/*
* Copyright (C) 2015-2022 Authlete, Inc.
*
* 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.authlete.jakarta;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import com.authlete.common.api.AuthleteApi;
import com.authlete.common.assurance.VerifiedClaims;
import com.authlete.common.assurance.constraint.VerifiedClaimsConstraint;
import com.authlete.common.assurance.constraint.VerifiedClaimsContainerConstraint;
import com.authlete.common.dto.AuthorizationFailRequest.Reason;
import com.authlete.common.dto.AuthorizationResponse;
import com.authlete.common.dto.Property;
import com.authlete.common.dto.StringArray;
import com.authlete.jakarta.spi.AuthorizationDecisionHandlerSpi;
/**
* Handler for end-user's decision on the authorization request.
*
*
* An authorization endpoint returns an authorization page (HTML) to an end-user,
* and the end-user will select either "authorize" or "deny" the authorization
* request. This class handles the decision and calls Authlete's
* {@code /api/auth/authorization/issue} API or {@code /api/auth/authorization/fail}
* API accordingly.
*
*
* @author Takahiko Kawasaki
*/
public class AuthorizationDecisionHandler extends BaseHandler
{
/**
* Parameters for this handler.
*
* @since 2.25
*/
public static class Params implements Serializable
{
private static final long serialVersionUID = 3L;
private String ticket;
private String[] claimNames;
private String[] claimLocales;
private String idTokenClaims;
private String[] requestedClaimsForTx;
private StringArray[] requestedVerifiedClaimsForTx;
private boolean oldIdaFormatUsed;
/**
* Get the ticket that was issued by Authlete's
* {@code /api/auth/authorization} API.
*
* @return
* The ticket that was issued by Authlete's
* {@code /api/auth/authorization} API.
*/
public String getTicket()
{
return ticket;
}
/**
* Set the ticket that was issued by Authlete's
* {@code /api/auth/authorization} API.
*
* @param ticket
* The ticket that was issued by Authlete's
* {@code /api/auth/authorization} API.
*
* @return
* {@code this} object.
*/
public Params setTicket(String ticket)
{
this.ticket = ticket;
return this;
}
/**
* Get the names of requested claims.
*
* @return
* The names of requested claims.
*/
public String[] getClaimNames()
{
return claimNames;
}
/**
* Set the names of requested claims. The value given to this method
* should be the value of the {@code claims} parameter in a response
* from Authlete's {@code /api/auth/authorization} API.
*
* @param names
* The names of requested claims.
*
* @return
* {@code this} object.
*/
public Params setClaimNames(String[] names)
{
this.claimNames = names;
return this;
}
/**
* Get the requested claim locales.
*
* @return
* Requested claim locales.
*/
public String[] getClaimLocales()
{
return this.claimLocales;
}
/**
* Set the requested claim locales. The value given to this method
* should be the value of the {@code claimsLocales} parameter in a
* response from Authlete's {@code /api/auth/authorization} API.
*
* @param locales
* Requested claim locales.
*
* @return
* {@code this} object.
*/
public Params setClaimLocales(String[] locales)
{
this.claimLocales = locales;
return this;
}
/**
* Get the value of the {@code id_token} property in the {@code claims}
* request parameter.
*
* @return
* Claims requested for an ID token.
*/
public String getIdTokenClaims()
{
return idTokenClaims;
}
/**
* Set the value of the {@code id_token} property in the {@code claims}
* request parameter. The value given to this method should be the
* value of the {@code idTokenClaims} parameter in a response from
* Authlete's {@code /api/auth/authorization} API.
*
* @param claims
* Claims requested for an ID token.
*
* @return
* {@code this} object.
*/
public Params setIdTokenClaims(String claims)
{
this.idTokenClaims = claims;
return this;
}
/**
* Get the claims that are indirectly requested by transformed claims.
*
* @return
* Claims requested by transformed claims.
*
* @see OpenID Connect Advanced Syntax for Claims (ASC) 1.0
*
* @since 2.41
*/
public String[] getRequestedClaimsForTx()
{
return requestedClaimsForTx;
}
/**
* Set the claims that are indirectly requested by transformed claims.
* The value given to this method should be the value of the
* {@code requestedClaimsForTx} parameter in a response from Authlete's
* {@code /api/auth/authorization} API.
*
* @param claims
* Claims requested by transformed claims.
*
* @return
* {@code this} object.
*
* @see OpenID Connect Advanced Syntax for Claims (ASC) 1.0
*
* @since 2.41
*/
public Params setRequestedClaimsForTx(String[] claims)
{
this.requestedClaimsForTx = claims;
return this;
}
/**
* Get the verified claims that are indirectly requested by transformed
* claims.
*
*
* See the JavaDoc of the
* getRequestedVerifiedClaimsForTx() method of
* AuthorizationResponse class for details about the format of
* the return value.
*
*
* @return
* Verified claims requested by transformed claims.
*
* @since 2.43
*
* @see OpenID Connect Advanced Syntax for Claims (ASC) 1.0
*
* @see AuthorizationResponse#getRequestedClaimsForTx()
*/
public StringArray[] getRequestedVerifiedClaimsForTx()
{
return requestedVerifiedClaimsForTx;
}
/**
* Set the verified claims that are indirectly requested by transformed
* claims. The value given to this method should be the value of the
* {@code requestedVerifiedClaimsForTx} parameter in a response from
* Authlete's {@code /api/auth/authorization} API.
*
*
* See the JavaDoc of the
* getRequestedVerifiedClaimsForTx() method of
* AuthorizationResponse class for details about the format of
* the argument.
*
*
* @param claims
* Verified claims requested by transformed claims.
*
* @return
* {@code this} object.
*
* @since 2.43
*
* @see OpenID Connect Advanced Syntax for Claims (ASC) 1.0
*
* @see AuthorizationResponse#getRequestedClaimsForTx()
*/
public Params setRequestedVerifiedClaimsForTx(StringArray[] claims)
{
this.requestedVerifiedClaimsForTx = claims;
return this;
}
/**
* Get the flag indicating whether {@link AuthorizationDecisionHandler}
* uses the old format of {@code "verified_claims"} defined in the
* Implementer's Draft 2 of OpenID Connect for Identity Assurance 1.0
* which was published on May 19, 2020.
*
*
* When this flag is on, {@link AuthorizationDecisionHandler} calls the
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* VerifiedClaimsConstraint) getVerifiedClaims(String, VerifiedClaimsConstraint)}
* method of {@link AuthorizationDecisionHandlerSpi}. On the other hand,
* if this flag is off, the
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String, Object)
* getVerifiedClaims(String, Object)} method is called instead. This is
* a breaking change from authlete-java-jaxrs version 2.41. This flag exists to mitigate
* the breaking change.
*
*
*
* The Implementer's Draft 3 of OpenID Connect for Identity Assurance 1.0,
* which was published on September 6, 2021, made many breaking changes.
* In addition, it is certain that further breaking changes will be made
* in the next draft. Considering the instability of the specification,
* it is not a good approach to define Java classes that correspond to
* elements in {@code "verified_claims"}. The
* {@code com.authlete.common.assurance} package in the authlete-java-common
* library was developed based on the approach for the Implementer's
* Draft 2, but it is not useful any more. This is the reason the
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* VerifiedClaimsConstraint) getVerifiedClaims(String,
* VerifiedClaimsConstraint)} method (whose second argument is an
* instance of {@link VerifiedClaimsConstraint} which is defined in
* the {@code com.authlete.common.assurance.constraint} package) was
* marked as deprecated.
*
*
* @return
* {@code true} if {@link AuthorizationDecisionHandler} calls
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* VerifiedClaimsConstraint) getVerifiedClaims(String,
* VerifiedClaimsConstraint)} method of
* {@link AuthorizationDecisionHandlerSpi}. {@code false} if
* {@link AuthorizationDecisionHandler} calls
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* Object) getVerifiedClaims(String, Object)} method instead.
*
* @since 2.42
*
* @see OpenID Connect for Identity Assurance 1.0
*/
public boolean isOldIdaFormatUsed()
{
return oldIdaFormatUsed;
}
/**
* Set the flag indicating whether {@link AuthorizationDecisionHandler}
* uses the old format of {@code "verified_claims"} defined in the
* Implementer's Draft 2 of OpenID Connect for Identity Assurance 1.0
* which was published on May 19, 2020.
*
*
* When this flag is on, {@link AuthorizationDecisionHandler} calls the
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* VerifiedClaimsConstraint) getVerifiedClaims(String, VerifiedClaimsConstraint)}
* method of {@link AuthorizationDecisionHandlerSpi}. On the other hand,
* if this flag is off, the
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String, Object)
* getVerifiedClaims(String, Object)} method is called instead. This is
* a breaking change from authlete-java-jaxrs version 2.41. This flag exists to mitigate
* the breaking change.
*
*
*
* The Implementer's Draft 3 of OpenID Connect for Identity Assurance 1.0,
* which was published on September 6, 2021, made many breaking changes.
* In addition, it is certain that further breaking changes will be made
* in the next draft. Considering the instability of the specification,
* it is not a good approach to define Java classes that correspond to
* elements in {@code "verified_claims"}. The
* {@code com.authlete.common.assurance} package in the authlete-java-common
* library was developed based on the approach for the Implementer's
* Draft 2, but it is not useful any more. This is the reason the
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* VerifiedClaimsConstraint) getVerifiedClaims(String,
* VerifiedClaimsConstraint)} method (whose second argument is an
* instance of {@link VerifiedClaimsConstraint} which is defined in
* the {@code com.authlete.common.assurance.constraint} package) was
* marked as deprecated.
*
*
* @param used
* {@code true} to make {@link AuthorizationDecisionHandler} call
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* VerifiedClaimsConstraint) getVerifiedClaims(String,
* VerifiedClaimsConstraint)} method of
* {@link AuthorizationDecisionHandlerSpi}. {@code false} to make
* {@link AuthorizationDecisionHandler} call
* {@link AuthorizationDecisionHandlerSpi#getVerifiedClaims(String,
* Object) getVerifiedClaims(String, Object)} method instead.
*
* @return
* {@code this} object.
*
* @since 2.42
*
* @see OpenID Connect for Identity Assurance 1.0
*/
public Params setOldIdaFormatUsed(boolean used)
{
this.oldIdaFormatUsed = used;
return this;
}
/**
* Create a {@link Params} instance from an instance of
* {@link AuthorizationResponse}.
*
* @param response
* An response from Authlete's {@code /api/auth/authorization} API.
*
* @return
* A new {@code Params} instance built from the response.
*/
public static Params from(AuthorizationResponse response)
{
return new Params()
.setTicket(response.getTicket())
.setClaimNames(response.getClaims())
.setClaimLocales(response.getClaimsLocales())
.setIdTokenClaims(response.getIdTokenClaims())
.setRequestedClaimsForTx(response.getRequestedClaimsForTx())
.setRequestedVerifiedClaimsForTx(response.getRequestedVerifiedClaimsForTx())
;
}
}
/**
* Implementation of {@link AuthorizationDecisionHandlerSpi} interface.
*/
private final AuthorizationDecisionHandlerSpi mSpi;
/**
* Constructor with an implementation of {@link AuthleteApi} interface
* and an implementation of {@link AuthorizationDecisionHandlerSpi}
* interface.
*
* @param api
* Implementation of {@link AuthleteApi} interface.
*
* @param spi
* Implementation of {@link AuthorizationDecisionHandlerSpi} interface.
*/
public AuthorizationDecisionHandler(AuthleteApi api, AuthorizationDecisionHandlerSpi spi)
{
super(api);
mSpi = spi;
}
/**
* Handle an end-user's decision on an authorization request.
*
* @param ticket
* A ticket that was issued by Authlete's {@code /api/auth/authorization} API.
*
* @param claimNames
* Names of requested claims. Use the value of the {@code claims}
* parameter in a response from Authlete's {@code /api/auth/authorization} API.
*
* @param claimLocales
* Requested claim locales. Use the value of the {@code claimsLocales}
* parameter in a response from Authlete's {@code /api/auth/authorization} API.
*
* @return
* A response to the client application. Basically, the response
* will trigger redirection to the client's redirection endpoint.
*
* @throws WebApplicationException
* An error occurred.
*/
public Response handle(String ticket, String[] claimNames, String[] claimLocales) throws WebApplicationException
{
Params params = new Params()
.setTicket(ticket)
.setClaimNames(claimNames)
.setClaimLocales(claimLocales)
;
return handle(params);
}
/**
* Handle an end-user's decision on an authorization request.
*
* @param params
* Parameters necessary to handle the decision.
*
* @return
* A response to the client application. Basically, the response
* will trigger redirection to the client's redirection endpoint.
*
* @throws WebApplicationException
* An error occurred.
*
* @since 2.25
*/
public Response handle(Params params) throws WebApplicationException
{
try
{
// Process the end-user's decision.
return process(params);
}
catch (WebApplicationException e)
{
throw e;
}
catch (Throwable t)
{
// Unexpected error.
throw unexpected("Unexpected error in AuthorizationDecisionHandler", t);
}
}
/**
* Process the end-user's decision.
*/
private Response process(Params params)
{
// If the end-user did not grant authorization to the client application.
if (mSpi.isClientAuthorized() == false)
{
// The end-user denied the authorization request.
return fail(params.getTicket(), Reason.DENIED);
}
// The subject (= unique identifier) of the end-user.
String subject = mSpi.getUserSubject();
// If the subject of the end-user is not available.
if (subject == null || subject.length() == 0)
{
// The end-user is not authenticated.
return fail(params.getTicket(), Reason.NOT_AUTHENTICATED);
}
// the potentially pairwise subject of the end user
String sub = mSpi.getSub();
// The time when the end-user was authenticated.
long authTime = mSpi.getUserAuthenticatedAt();
// The ACR (Authentication Context Class Reference) of the
// end-user authentication.
String acr = mSpi.getAcr();
// Collect claim values.
Map claims = collectClaims(
subject, params.getClaimNames(), params.getClaimLocales());
// Collect values of claims that are indirectly requested by transformed claims.
// See "OpenID Connect Advanced Syntax for Claims (ASC) 1.0" for details.
Map claimsForTx = collectClaims(
subject, params.getRequestedClaimsForTx(), params.getClaimLocales());
// Values of verified claims that are used to compute values of
// transformed claims under "verified_claims/claims".
List