
com.atlassian.asap.api.JwtBuilder Maven / Gradle / Ivy
package com.atlassian.asap.api;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* A fluent builder for constructing a {@link com.atlassian.asap.api.Jwt} object.
*/
public final class JwtBuilder
{
/**
* Token lifetime, unless a specific lifetime is explicitly set. This is the time span between the
* iat and the exp claims.
*/
public static final Duration DEFAULT_LIFETIME = Duration.ofSeconds(60);
private SigningAlgorithm alg;
private String keyId;
private String iss;
private Optional sub;
private Iterable aud;
private Instant iat;
private Instant exp;
private Optional nbf;
private String jti;
private JwtBuilder()
{
Instant now = Instant.now();
notBefore(Optional.of(now));
issuedAt(now);
expirationTime(now.plus(DEFAULT_LIFETIME));
jwtId(UUID.randomUUID().toString());
algorithm(SigningAlgorithm.RS256);
sub = Optional.empty();
}
/**
* Construct a simple jwt builder initialised with default claim values as follows:
*
* - nbf, iat claim set to current system time
* - exp claim set current time plus default expiry as defined in {@link JwtBuilder#DEFAULT_LIFETIME}
* - jti claim set to a random UUID.
* - alg header set to {@link SigningAlgorithm#RS256}.
*
* @return a new fluent builder
*/
public static JwtBuilder newJwt()
{
return new JwtBuilder();
}
/**
* Returns a builder initialised with the given Jwt prototype.
*
* @param prototype Jwt to use as prototype
* @return a Jwt builder initialised to be a copy of the prototype
*/
public static JwtBuilder copyJwt(Jwt prototype)
{
return new JwtBuilder()
.algorithm(prototype.getHeader().getAlgorithm())
.keyId(prototype.getHeader().getKeyId())
.issuer(prototype.getClaims().getIssuer())
.subject(prototype.getClaims().getSubject())
.audience(prototype.getClaims().getAudience())
.issuedAt(prototype.getClaims().getIssuedAt())
.expirationTime(prototype.getClaims().getExpiry())
.notBefore(prototype.getClaims().getNotBefore())
.jwtId(prototype.getClaims().getJwtId());
}
/**
* Sets the key id jws header.
* @param keyId the key id for the jws header of this jwt
* @return the fluent builder
*/
public JwtBuilder keyId(String keyId)
{
this.keyId = keyId;
return this;
}
/**
* Sets the algorithm (alg) jws header.
* @param alg the alg for the jws header of this jwt
* @return the fluent builder
*/
public JwtBuilder algorithm(SigningAlgorithm alg)
{
this.alg = alg;
return this;
}
/**
* Sets the audience (aud) claim.
* @param aud an iterable containing one or more audiences for the jwt
* @return the fluent builder
*/
public JwtBuilder audience(Iterable aud)
{
this.aud = ImmutableSet.copyOf(aud);
return this;
}
/**
* Sets the audience (aud) claim.
* @param aud one or more audiences for the jwt
* @return the fluent builder
*/
public JwtBuilder audience(String... aud)
{
this.aud = ImmutableSet.copyOf(aud);
return this;
}
/**
* Sets the expiration time (exp) claim.
* @param expiry the expiration time
* @return the fluent builder
*/
public JwtBuilder expirationTime(Instant expiry)
{
this.exp = expiry;
return this;
}
/**
* Set the issued at (iat) claim.
* @param iat the issued at time
* @return the fluent builder
*/
public JwtBuilder issuedAt(Instant iat)
{
this.iat = iat;
return this;
}
/**
* Set the issuer (iss) claim.
* @param iss the issuer
* @return the fluent builder
*/
public JwtBuilder issuer(String iss)
{
this.iss = iss;
return this;
}
/**
* Set the jwt id (jti) claim.
* @param jti a unique id for the jwt
* @return the fluent builder
*/
public JwtBuilder jwtId(String jti)
{
this.jti = jti;
return this;
}
/**
* Set the not before (nbf) claim.
* @param nbf the not before date
* @return the fluent builder
*/
public JwtBuilder notBefore(Optional nbf)
{
this.nbf = nbf;
return this;
}
/**
* Sets the subject (sub) claim for this jwt.
* @param sub the subject
* @return the fluent builder
*/
public JwtBuilder subject(Optional sub)
{
this.sub = sub;
return this;
}
/**
* @return a JWT object representing all the values specified to this builder
* @throws java.lang.NullPointerException if some required parameter has not been specified
*/
public Jwt build()
{
JwsHeader header = new ImmutableJwsHeader(alg, keyId);
JwtClaims claims = new ImmutableJwtClaims(iss, sub, aud, exp, nbf, iat, jti);
return new ImmutableJwt(header, claims);
}
/**
* An immutable value object that represents a JWT.
*/
private static class ImmutableJwt implements Jwt
{
private final JwsHeader header;
private final JwtClaims claimsSet;
ImmutableJwt(JwsHeader header, JwtClaims claims)
{
this.header = Objects.requireNonNull(header);
this.claimsSet = Objects.requireNonNull(claims);
}
@Override
public JwsHeader getHeader()
{
return header;
}
@Override
public JwtClaims getClaims()
{
return claimsSet;
}
@Override
public String toString()
{
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("header", header)
.append("claims", claimsSet)
.toString();
}
@Override
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (o.getClass() != getClass())
{
return false;
}
ImmutableJwt rhs = (ImmutableJwt) o;
return new EqualsBuilder()
.append(header, rhs.header)
.append(claimsSet, rhs.claimsSet)
.isEquals();
}
@Override
public int hashCode()
{
return new HashCodeBuilder()
.append(header)
.append(claimsSet)
.hashCode();
}
}
/**
* An immutable value object that represents the information contained in the JWS header.
*/
private static final class ImmutableJwsHeader implements JwsHeader
{
private final SigningAlgorithm algorithm;
private final String keyId;
ImmutableJwsHeader(SigningAlgorithm algorithm, String keyId)
{
this.algorithm = Objects.requireNonNull(algorithm, "JWT header 'alg' cannot be null");
this.keyId = Objects.requireNonNull(keyId, "JWT header 'kid' cannot be null");
}
@Override
public String getKeyId()
{
return keyId;
}
@Override
public SigningAlgorithm getAlgorithm()
{
return algorithm;
}
@Override
public String toString()
{
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append(Header.ALGORITHM.key(), algorithm)
.append(Header.KEY_ID.key(), keyId)
.toString();
}
@Override
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (o.getClass() != getClass())
{
return false;
}
ImmutableJwsHeader rhs = (ImmutableJwsHeader) o;
return new EqualsBuilder()
.append(algorithm, rhs.algorithm)
.append(keyId, rhs.keyId)
.isEquals();
}
@Override
public int hashCode()
{
return new HashCodeBuilder()
.append(algorithm)
.append(keyId)
.hashCode();
}
}
/**
* An immutable value object that represents the information contained in the claims (payload) of a JWT.
*/
private static class ImmutableJwtClaims implements JwtClaims
{
private final String iss;
private final Optional sub;
private final Set aud;
private final Instant expiry;
private final Optional notBefore;
private final Instant issuedAt;
private final String jwtId;
ImmutableJwtClaims(String iss,
Optional sub,
Iterable aud,
Instant exp,
Optional nbf,
Instant iat,
String jti)
{
this.iss = Objects.requireNonNull(iss, "JWT claim 'iss' cannot be null");
this.sub = Objects.requireNonNull(sub, "JWT claim 'sub' cannot be null (but it can be None)");
this.aud = ImmutableSet.copyOf(Objects.requireNonNull(aud, "JWT claim 'aud' cannot be null"));
this.issuedAt = Objects.requireNonNull(iat, "JWT claim 'iat' cannot be null");
this.expiry = Objects.requireNonNull(exp, "JWT claim 'exp' cannot be null");
this.notBefore = Objects.requireNonNull(nbf, "JWT claim 'nbf' cannot be null (but it can be None)");
this.jwtId = Objects.requireNonNull(jti, "JWT claim 'jit' cannot be null");
}
@Override
public String getIssuer()
{
return iss;
}
@Override
public Optional getSubject()
{
return sub;
}
@Override
public Set getAudience()
{
return aud;
}
@Override
public Instant getExpiry()
{
return expiry;
}
@Override
public Optional getNotBefore()
{
return notBefore;
}
@Override
public Instant getIssuedAt()
{
return issuedAt;
}
@Override
public String getJwtId()
{
return jwtId;
}
@Override
public String toString()
{
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append(Claim.ISSUER.key(), iss)
.append(Claim.SUBJECT.key(), sub)
.append(Claim.AUDIENCE.key(), aud)
.append(Claim.ISSUED_AT.key(), issuedAt)
.append(Claim.EXPIRY.key(), expiry)
.append(Claim.NOT_BEFORE.key(), notBefore)
.append(Claim.JWT_ID.key(), jwtId)
.toString();
}
@Override
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (o.getClass() != getClass())
{
return false;
}
ImmutableJwtClaims rhs = (ImmutableJwtClaims) o;
return new EqualsBuilder()
.append(iss, rhs.iss)
.append(sub, rhs.sub)
.append(aud, rhs.aud)
.append(issuedAt, rhs.issuedAt)
.append(expiry, rhs.expiry)
.append(notBefore, rhs.notBefore)
.append(jwtId, rhs.jwtId)
.isEquals();
}
@Override
public int hashCode()
{
return new HashCodeBuilder()
.append(iss)
.append(sub)
.append(aud)
.append(issuedAt)
.append(expiry)
.append(notBefore)
.append(jwtId)
.hashCode();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy