Skip to content

Commit

Permalink
Feature: Include additional attributes on SSA Get endpoint (#3983)
Browse files Browse the repository at this point in the history
* feat(jans-auth-server): added custom attributes in create ssa, get ssa and get jwt from ssa.
description, one_time_use and rotate_ssa added in get ssa endpoint.

* feat(docs): update site docs for ssa
  • Loading branch information
Milton-Ch authored Feb 27, 2023
1 parent dfed3b5 commit 4fded3e
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 4 deletions.
80 changes: 79 additions & 1 deletion docs/admin/auth-server/endpoints/ssa.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Returned SSA is a JWT, containing the following structure:
- `software_roles` — List of string values, fixed value `["password", "notify"]`.
- `grant_types` — Fixed value `["client_credentials"]`.
- `exp` — Expiration Time.
- `myCustom1, myCustom2, ...`: if you have custom attributes, they will be displayed here.

### Example:

Expand Down Expand Up @@ -214,6 +215,9 @@ Get existing active SSA based on `jti` or `org_id`.
"iss": "https://jans.localhost",
"exp": 1668608852,
"iat": 1668608851,
"description: "test description",
"one_time_use": true,
"rotate_ssa": false
},
"iss": "ed4d5f74-ce41-4180-aed4-54cffa974630",
"created_at": 1668608851,
Expand All @@ -232,6 +236,10 @@ Get existing active SSA based on `jti` or `org_id`.
- `iss` — The "iss" (issuer) claim identifies the principal that issued the JWT.
- `exp` — Expiration time.
- `iat` — Creation time.
- `description` — Describe SSA.
- `one_time_use` — Defined whether the SSA will be used only once or can be used multiple times.
- `rotate_ssa` — TODO - Will be used to rotate expiration of the SSA, currently is only saved as part of the SSA.
- `myCustom1, myCustom2, ...` — if you have custom attributes, they will be displayed here.
- `iss` — The "iss" is related to the client that created this SSA.
- `created_at` — Creation time.
- `expiration` — Expiration time.
Expand Down Expand Up @@ -287,7 +295,10 @@ Connection: Keep-Alive
],
"exp": 1668608852,
"iat": 1668608851,
"jti": "c3eb1c16-be9b-4e96-974e-aea5e3cf95b0"
"jti": "c3eb1c16-be9b-4e96-974e-aea5e3cf95b0",
"description: "test description",
"one_time_use": true,
"rotate_ssa": false
},
"iss": "ed4d5f74-ce41-4180-aed4-54cffa974630",
"created_at": 1668608851,
Expand All @@ -297,6 +308,73 @@ Connection: Keep-Alive
]
```

## Get JWT SSA

Get existing active SSA based on `jti`.

### Query Parameters

- `jti` — Unique identifier

### Response description

Returned SSA is a JWT, containing the following structure:

```
{
"ssa": "eyJraWQiOiI1NTk3MGFkZS00M2MwLTQ4YWMtODEyZi0yZTY1MzhjMTEyN2Zfc2lnX3JzNTEyIiwidHlwIjoiand0IiwiYWxnIjoiUlM1MTIifQ.eyJzb2Z0d2FyZV9pZCI6ImdsdXUtc2Nhbi1hcGkiLCJncmFudF90eXBlcyI6WyJjbGllbnRfY3JlZGVudGlhbHMiXSwib3JnX2lkIjoxLCJpc3MiOiJodHRwczovL2phbnMubG9jYWxob3N0Iiwic29mdHdhcmVfcm9sZXMiOlsicGFzc3d1cmQiXSwiZXhwIjoxNjY4NjA5MDA1LCJpYXQiOjE2Njg2NDE5NjcsImp0aSI6ImU4OWVjYTQxLTM0ODUtNDUxNi1hMTYyLWZiODYyNjJhYmFjMyJ9.jRgh8_aiwMTJxeT9cwfup9QP9LBc6gQstvabCzUOJvELnzosxiNJHeU2mrvavaNK6BGvs_lbNjODVDeetGCD48_F2ay9r8qmo-f3GPzdzcJozKgfzonSkAE5Ran9LKcQQJpVc1rDYcV2xYiJLJ6FSuvnoClkDEE1tXysxshLPs-GXOZE7rD8XUXzezuxZWUE1jXwA-EFajoat8CP6QulHGxlcn_sKIhawhGODxJPz4Pf3jgeZVLG_7HfRJgxNiKcdzQIxnkbdpuS-0Q4-oc5yntsXhFhn31Pa3vGsiPPH9f3ndL2ZZKk3xCgyImLDJuGaxXg-qEVoIG4zNWNHMUNUQ"
}
```

**Header**

- `alg` — The signature algorithm which used `RS256`.
- `typ` — The type which used.
- `kid` — The key identification `gluu-scan-api-rs256-ssa-signature-key`.

**Payload**

- `iat` — The time the JWT was created.
- `iss` — The "iss" (issuer) claim identifies the principal that issued the JWT.
- `jti` — The "jti" (JWT ID) claim provides a unique identifier for the JWT.
- `software_id` — The "software_id" is used for software identification.
- `org_id` — The "org_id" is used for organization identification.
- `software_roles` — List of string values, fixed value `["password", "notify"]`.
- `grant_types` — Fixed value `["client_credentials"]`.
- `exp` — Expiration Time.
- `myCustom1, myCustom2, ...`: if you have custom attributes, they will be displayed here.

### Example:

**Request:**

```
GET {{your-url}}/ssa/jwt?jti={{your-jti}}
Content-Type: application/json
Authorization: Bearer {{your-token}}
```

**Response:**

```
HTTP/1.1 201 Created
Date: Wed, 16 Nov 2022 23:39:27 GMT
Server: Apache/2.4.52 (Ubuntu)
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains
Cache-Control: no-store
Content-Type: application/json
Pragma: no-cache
Content-Length: 757
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
{
"ssa": "eyJraWQiOiI1NTk3MGFkZS00M2MwLTQ4YWMtODEyZi0yZTY1MzhjMTEyN2Zfc2lnX3JzNTEyIiwidHlwIjoiand0IiwiYWxnIjoiUlM1MTIifQ.eyJzb2Z0d2FyZV9pZCI6ImdsdXUtc2Nhbi1hcGkiLCJncmFudF90eXBlcyI6WyJjbGllbnRfY3JlZGVudGlhbHMiXSwib3JnX2lkIjoxLCJpc3MiOiJodHRwczovL2phbnMubG9jYWxob3N0Iiwic29mdHdhcmVfcm9sZXMiOlsicGFzc3d1cmQiXSwiZXhwIjoxNjY4NjA5MDA1LCJpYXQiOjE2Njg2NDE5NjcsImp0aSI6ImU4OWVjYTQxLTM0ODUtNDUxNi1hMTYyLWZiODYyNjJhYmFjMyJ9.jRgh8_aiwMTJxeT9cwfup9QP9LBc6gQstvabCzUOJvELnzosxiNJHeU2mrvavaNK6BGvs_lbNjODVDeetGCD48_F2ay9r8qmo-f3GPzdzcJozKgfzonSkAE5Ran9LKcQQJpVc1rDYcV2xYiJLJ6FSuvnoClkDEE1tXysxshLPs-GXOZE7rD8XUXzezuxZWUE1jXwA-EFajoat8CP6QulHGxlcn_sKIhawhGODxJPz4Pf3jgeZVLG_7HfRJgxNiKcdzQIxnkbdpuS-0Q4-oc5yntsXhFhn31Pa3vGsiPPH9f3ndL2ZZKk3xCgyImLDJuGaxXg-qEVoIG4zNWNHMUNUQ"
}
```

## Validate SSA

Validate existing active SSA based on `jti`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public JSONObject getJSONParameters() throws JSONException {
@Override
public String toString() {
return "SsaRequest{" +
"orgId=" + orgId +
"orgId='" + orgId + '\'' +
", expiration=" + expiration +
", description='" + description + '\'' +
", softwareId='" + softwareId + '\'' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ public JSONArray getJSONArray(List<Ssa> ssaList) throws JSONException {
Util.addToJSONObjectIfNotNull(jsonSsa, IAT.getName(), DateUtil.dateToUnixEpoch(ssa.getCreationDate()));
Util.addToJSONObjectIfNotNull(jsonSsa, EXP.getName(), DateUtil.dateToUnixEpoch(ssa.getExpirationDate()));
Util.addToJSONObjectIfNotNull(jsonSsa, JTI.getName(), ssa.getId());
Util.addToJSONObjectIfNotNull(jsonSsa, DESCRIPTION.getName(), ssa.getDescription());
Util.addToJSONObjectIfNotNull(jsonSsa, ONE_TIME_USE.getName(), ssa.getAttributes().getOneTimeUse());
Util.addToJSONObjectIfNotNull(jsonSsa, ROTATE_SSA.getName(), ssa.getAttributes().getRotateSsa());
if (!ssa.getAttributes().getCustomAttributes().isEmpty()) {
ssa.getAttributes().getCustomAttributes().forEach((key, value) -> Util.addToJSONObjectIfNotNull(jsonSsa, key, value));
}

Util.addToJSONObjectIfNotNull(responseJsonObject, SSA.getName(), jsonSsa);
jsonArray.put(responseJsonObject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ public Client getClientFromSession() throws WebApplicationException {
*/
public void checkScopesPolicy(Client client, String scope) throws WebApplicationException {
List<String> scopes = scopeService.getScopeIdsByDns(Arrays.stream(client.getScopes()).collect(Collectors.toList()));
if (!scopes.contains(scope))
if (!scopes.contains(scope)) {
throw errorResponseFactory.createWebApplicationException(Response.Status.UNAUTHORIZED, SsaErrorResponseType.UNAUTHORIZED_CLIENT, "Unauthorized client");
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public Jwt generateJwt(Ssa ssa, ExecutionContext executionContext) throws Except
public Jwt generateJwt(Ssa ssa) throws Exception {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(appConfiguration.getSsaConfiguration().getSsaSigningAlg());
if (signatureAlgorithm == null) {
log.error("Invalid signature, Key is not found: {}", appConfiguration.getSsaConfiguration().getSsaSigningAlg());
log.error("Invalid signature algorithm, not found: {}", appConfiguration.getSsaConfiguration().getSsaSigningAlg());
throw errorResponseFactory.createWebApplicationException(Response.Status.BAD_REQUEST, SsaErrorResponseType.INVALID_SIGNATURE, "Invalid signature error");
}
String keyId = cryptoProvider.getKeyId(webKeysConfiguration, signatureAlgorithm.getAlg(), Use.SIGNATURE, KeyOpsType.SSA);
Expand All @@ -188,6 +188,9 @@ public Jwt generateJwt(Ssa ssa) throws Exception {
jwt.getClaims().setClaim(ORG_ID.getName(), ssa.getOrgId());
jwt.getClaims().setClaim(SOFTWARE_ROLES.getName(), ssa.getAttributes().getSoftwareRoles());
jwt.getClaims().setClaim(GRANT_TYPES.getName(), ssa.getAttributes().getGrantTypes());
if (!ssa.getAttributes().getCustomAttributes().isEmpty()) {
ssa.getAttributes().getCustomAttributes().forEach((key, value) -> jwt.getClaims().setClaim(key, value));
}

String signature = cryptoProvider.sign(jwt.getSigningInput(), jwt.getHeader().getKeyId(), null, signatureAlgorithm);
jwt.setEncodedSignature(signature);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,20 @@ public void generateJwt_signatureAlgorithmNull_invalidSignature() {
verifyNoInteractions(cryptoProvider, webKeysConfiguration);
}

@Test
public void generateJwt_customAttributesEmpty_jwtValid() throws Exception {
SsaConfiguration ssaConfiguration = new SsaConfiguration();
String issuer = "https://test.jans.io";
ssa.getAttributes().setCustomAttributes(Collections.singletonMap("test-key", "test-value"));
when(appConfiguration.getSsaConfiguration()).thenReturn(ssaConfiguration);
when(appConfiguration.getIssuer()).thenReturn(issuer);

Jwt jwt = ssaService.generateJwt(ssa);
assertSsaJwt(ssaConfiguration.getSsaSigningAlg(), issuer, ssa, jwt);
verify(cryptoProvider).sign(any(), any(), eq(null), any());
verifyNoInteractions(log);
}

@Test
public void createNotAcceptableResponse_valid_response() {
Response response = ssaService.createNotAcceptableResponse().build();
Expand Down Expand Up @@ -329,6 +343,11 @@ private static void assertSsaJwt(String ssaSigningAlg, String issuer, Ssa ssa, J
assertEquals(jwtClaims.getClaim(IAT.getName()), ssa.getCreationDate());
assertNotNull(jwtClaims.getClaim(EXP.getName()), "The exp in jwt is null");
assertEquals(jwtClaims.getClaim(EXP.getName()), ssa.getExpirationDate());

ssa.getAttributes().getCustomAttributes().forEach((key, value) -> {
assertTrue(jwtClaims.hasClaim(key));
assertEquals(jwtClaims.getClaimAsString(key), value);
});
}

private static void assertSsaWithAux(Ssa ssa, Ssa ssaAux) {
Expand Down

0 comments on commit 4fded3e

Please sign in to comment.