1 package com.atlassian.asap.nimbus.serializer;
2
3 import com.atlassian.asap.api.Jwt;
4 import com.atlassian.asap.api.JwtBuilder;
5 import com.atlassian.asap.api.JwtClaims.RegisteredClaim;
6 import com.atlassian.asap.api.SigningAlgorithm;
7 import com.atlassian.asap.core.SecurityProvider;
8 import com.google.common.collect.ImmutableList;
9 import com.google.common.collect.ImmutableSet;
10 import com.nimbusds.jose.JWSAlgorithm;
11 import com.nimbusds.jose.JWSHeader;
12 import com.nimbusds.jose.JWSObject;
13 import com.nimbusds.jose.JWSSigner;
14 import com.nimbusds.jose.util.Base64URL;
15 import org.junit.Test;
16 import org.junit.runner.RunWith;
17 import org.mockito.Matchers;
18 import org.mockito.Mock;
19 import org.mockito.runners.MockitoJUnitRunner;
20
21 import javax.json.Json;
22 import java.security.interfaces.ECPrivateKey;
23 import java.security.interfaces.RSAPrivateKey;
24 import java.time.Instant;
25 import java.util.Optional;
26
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.mockito.Matchers.any;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32
33 @RunWith(MockitoJUnitRunner.class)
34 public class NimbusJwtSerializerTest {
35 private static final String KID = "my-key";
36 private static final String ISSUER = "my-issuer";
37 private static final String SUBJECT = "my-subject";
38 private static final String AUDIENCE = "my-audience";
39 private static final String TOKEN_ID = "my-id";
40 private static final String SIGNATURE = "valid-signature";
41 private static final long NOT_BEFORE_SECONDS = 1L;
42 private static final long ISSUED_AT_SECONDS = 2L;
43 private static final long EXPIRY_SECONDS = 3L;
44 private static final Jwt BASE_JWT = JwtBuilder.newJwt()
45 .algorithm(SigningAlgorithm.RS256)
46 .keyId(KID)
47 .issuer(ISSUER)
48 .audience(AUDIENCE)
49 .jwtId(TOKEN_ID)
50 .issuedAt(Instant.ofEpochSecond(ISSUED_AT_SECONDS))
51 .expirationTime(Instant.ofEpochSecond(EXPIRY_SECONDS))
52 .notBefore(Optional.of(Instant.ofEpochSecond(NOT_BEFORE_SECONDS)))
53 .build();
54
55 @Mock
56 private RSAPrivateKey rsaPrivateKey;
57 @Mock
58 private ECPrivateKey ecPrivateKey;
59 @Mock
60 private JWSSigner signer;
61
62 @Test
63 public void tokenShouldBeSignedWhenUsingRs256() throws Exception {
64 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
65
66 assertEquals(SIGNATURE, signedJwsObject.getSignature().decodeToString());
67 verify(signer).sign(any(JWSHeader.class), Matchers.<byte[]>any());
68 }
69
70 @Test
71 public void tokenShouldBeSignedWhenUsingEs256() throws Exception {
72 Jwt jwt = JwtBuilder.copyJwt(BASE_JWT).algorithm(SigningAlgorithm.ES256).build();
73 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(jwt, ecPrivateKey);
74
75 assertEquals(SIGNATURE, signedJwsObject.getSignature().decodeToString());
76 verify(signer).sign(any(JWSHeader.class), Matchers.<byte[]>any());
77 }
78
79 @Test
80 public void tokenShouldIncludeKidInTheHeader() throws Exception {
81 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
82
83 assertEquals(KID, signedJwsObject.getHeader().getKeyID());
84 }
85
86 @Test
87 public void tokenShouldIncludeAlgorithmInTheHeader() throws Exception {
88 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
89
90 assertEquals(JWSAlgorithm.RS256, signedJwsObject.getHeader().getAlgorithm());
91 }
92
93 @Test
94 public void tokenShouldIncludeIssuerInThePayload() throws Exception {
95 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
96
97 assertEquals(ISSUER, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.ISSUER.key()));
98 }
99
100 @Test
101 public void tokenShouldIncludeSubjectInThePayloadIfSpecified() throws Exception {
102 Jwt jwt = JwtBuilder.copyJwt(BASE_JWT).subject(Optional.of(SUBJECT)).build();
103 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(jwt, rsaPrivateKey);
104
105 assertEquals(SUBJECT, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.SUBJECT.key()));
106 }
107
108 @Test
109 public void tokenShouldNotIncludeSubjectInThePayloadIfNotSpecified() throws Exception {
110 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
111
112 assertFalse(signedJwsObject.getPayload().toJSONObject().containsKey(RegisteredClaim.SUBJECT.key()));
113 }
114
115 @Test
116 public void tokenShouldIncludeSingleAudienceInThePayload() throws Exception {
117 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
118
119 assertEquals(AUDIENCE, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.AUDIENCE.key()));
120 }
121
122 @Test
123 public void tokenShouldIncludeMultipleAudienceInThePayload() throws Exception {
124 Jwt jwt = JwtBuilder.copyJwt(BASE_JWT).audience(AUDIENCE, "another-audience").build();
125 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(jwt, rsaPrivateKey);
126
127 assertEquals(ImmutableList.of(AUDIENCE, "another-audience"),
128 signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.AUDIENCE.key()));
129 }
130
131 @Test
132 public void tokenShouldIncludeTokenIdInThePayload() throws Exception {
133 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
134
135 assertEquals(TOKEN_ID, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.JWT_ID.key()));
136 }
137
138 @Test
139 public void tokenShouldIncludeIssuedAtInThePayload() throws Exception {
140 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
141
142 assertEquals(ISSUED_AT_SECONDS, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.ISSUED_AT.key()));
143 }
144
145 @Test
146 public void tokenShouldIncludeExpiryInThePayload() throws Exception {
147 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
148
149 assertEquals(EXPIRY_SECONDS, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.EXPIRY.key()));
150 }
151
152 @Test
153 public void tokenShouldIncludeNotBeforeInThePayloadIfSpecified() throws Exception {
154 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(BASE_JWT, rsaPrivateKey);
155
156 assertEquals(NOT_BEFORE_SECONDS, signedJwsObject.getPayload().toJSONObject().get(RegisteredClaim.NOT_BEFORE.key()));
157 }
158
159 @Test
160 public void tokenShouldNotIncludeNotBeforeInThePayloadIfNotSpecified() throws Exception {
161 Jwt jwt = JwtBuilder.copyJwt(BASE_JWT).notBefore(Optional.<Instant>empty()).build();
162 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(jwt, rsaPrivateKey);
163
164 assertFalse(signedJwsObject.getPayload().toJSONObject().containsKey(RegisteredClaim.NOT_BEFORE.key()));
165 }
166
167 @Test
168 public void tokenShouldIncludePrivateClaimInThePayloadIfSpecified() throws Exception {
169 Jwt jwt = JwtBuilder.copyJwt(BASE_JWT)
170 .customClaims(Json.createObjectBuilder().add("privateClaim", "privateClaimValue").build())
171 .build();
172 JWSObject signedJwsObject = createSerializer().getSignedJwsObject(jwt, rsaPrivateKey);
173
174 assertEquals("privateClaimValue", signedJwsObject.getPayload().toJSONObject().get("privateClaim"));
175 }
176
177 private NimbusJwtSerializer createSerializer() throws Exception {
178 when(signer.supportedJWSAlgorithms()).thenReturn(ImmutableSet.of(JWSAlgorithm.RS256, JWSAlgorithm.ES256));
179 when(signer.sign(any(JWSHeader.class), Matchers.any())).thenReturn(Base64URL.encode(SIGNATURE));
180
181 return new NimbusJwtSerializer(SecurityProvider.getProvider()) {
182 @Override
183 protected JWSSigner createRSASSASignerForKey(RSAPrivateKey privateKey) {
184
185 return signer;
186 }
187
188 @Override
189 protected JWSSigner createECDSASignerForKey(ECPrivateKey privateKey) {
190
191 return signer;
192 }
193 };
194 }
195 }