View Javadoc

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                 // we extend this factory method to replace the collaborator with a mock
185                 return signer;
186             }
187 
188             @Override
189             protected JWSSigner createECDSASignerForKey(ECPrivateKey privateKey) {
190                 // we extend this factory method to replace the collaborator with a mock
191                 return signer;
192             }
193         };
194     }
195 }