1 package com.atlassian.asap.nimbus.parser;
2
3 import com.atlassian.asap.api.JwsHeader.Header;
4 import com.atlassian.asap.api.JwtClaims.RegisteredClaim;
5 import com.atlassian.asap.core.SecurityProvider;
6 import com.atlassian.asap.core.exception.JwtParseException;
7 import com.atlassian.asap.core.exception.MissingRequiredClaimException;
8 import com.atlassian.asap.core.exception.MissingRequiredHeaderException;
9 import com.atlassian.asap.core.exception.UnsupportedAlgorithmException;
10 import com.atlassian.asap.core.parser.JwtParser;
11 import com.atlassian.asap.core.parser.VerifiableJwt;
12 import com.nimbusds.jose.Algorithm;
13 import com.nimbusds.jose.JWSObject;
14 import com.nimbusds.jwt.JWTClaimsSet;
15 import net.minidev.json.JSONObject;
16
17 import java.security.Provider;
18 import java.text.ParseException;
19 import java.util.Collection;
20 import java.util.Optional;
21
22
23
24
25 public class NimbusJwtParser implements JwtParser {
26
27 private final Provider provider;
28
29 public NimbusJwtParser() {
30 this(SecurityProvider.getProvider());
31 }
32
33 public NimbusJwtParser(final Provider provider) {
34 this.provider = provider;
35 }
36
37 @Override
38 public VerifiableJwt parse(final String serializedJwt) throws JwtParseException, UnsupportedAlgorithmException {
39 final JWSObject jwsObject;
40 final JWTClaimsSet claims;
41
42 try {
43 jwsObject = JWSObject.parse(serializedJwt);
44
45 validateRequiredHeaders(jwsObject);
46
47 JSONObject jsonPayload = jwsObject.getPayload().toJSONObject();
48 claims = JWTClaimsSet.parse(jsonPayload);
49 } catch (ParseException e) {
50 throw new JwtParseException(e);
51 }
52
53 validateRequiredClaims(claims);
54 return NimbusVerifiableJwt.buildVerifiableJwt(jwsObject, claims, provider);
55 }
56
57 @Override
58 public Optional<String> determineUnverifiedIssuer(String serializedJwt) {
59 try {
60 JWSObject jwsObject = JWSObject.parse(serializedJwt);
61 JSONObject jsonPayload = jwsObject.getPayload().toJSONObject();
62 JWTClaimsSet claims = JWTClaimsSet.parse(jsonPayload);
63 return Optional.ofNullable(claims.getIssuer());
64 } catch (ParseException e) {
65 return Optional.empty();
66 }
67 }
68
69 private void validateRequiredHeaders(JWSObject jwsObject) throws MissingRequiredHeaderException {
70 if (jwsObject.getHeader().getAlgorithm() == null || jwsObject.getHeader().getAlgorithm() == Algorithm.NONE) {
71 throw new MissingRequiredHeaderException(Header.ALGORITHM);
72 }
73
74 if (jwsObject.getHeader().getKeyID() == null) {
75 throw new MissingRequiredHeaderException(Header.KEY_ID);
76 }
77 }
78
79 private void validateRequiredClaims(JWTClaimsSet claims) throws MissingRequiredClaimException {
80 checkClaimNotEmpty(claims.getAudience(), RegisteredClaim.AUDIENCE);
81 checkClaimNotNull(claims.getIssuer(), RegisteredClaim.ISSUER);
82 checkClaimNotNull(claims.getJWTID(), RegisteredClaim.JWT_ID);
83 checkClaimNotNull(claims.getIssueTime(), RegisteredClaim.ISSUED_AT);
84 checkClaimNotNull(claims.getExpirationTime(), RegisteredClaim.EXPIRY);
85 }
86
87 private static void checkClaimNotNull(Object claimValue, RegisteredClaim claim) throws MissingRequiredClaimException {
88 if (claimValue == null) {
89 throw new MissingRequiredClaimException(claim);
90 }
91 }
92
93 private static void checkClaimNotEmpty(Collection claimValue, RegisteredClaim claim) throws MissingRequiredClaimException {
94 if (claimValue == null || claimValue.isEmpty()) {
95 throw new MissingRequiredClaimException(claim);
96 }
97 }
98 }