Skip to content

Commit 7557478

Browse files
committed
Add support for at+jwt profile.
This adds support for the at-jwt type (RFC9068), indicated and detectable from the "typ" header claim as outlined in RFC8725 Section 3.11. Used by Unity IAM which is used e.g. by the Helmholtz AAI.
1 parent ff0c4fd commit 7557478

File tree

4 files changed

+38
-7
lines changed

4 files changed

+38
-7
lines changed

src/create.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const char usage[] = \
2323
" -k | --key <key_file> File containing the signing private key.\n"
2424
" -K | --keyid <kid> Name of the token key.\n"
2525
" -i | --issuer <issuer> Issuer for the token.\n"
26-
" -p | --profile <profile> Token profile (wlcg, scitokens1, scitokens2).\n"
26+
" -p | --profile <profile> Token profile (wlcg, scitokens1, scitokens2, atjwt).\n"
2727
"\n";
2828

2929
const struct option long_options[] =
@@ -180,6 +180,8 @@ int main(int argc, char *argv[]) {
180180
profile = SciTokenProfile::SCITOKENS_1_0;
181181
} else if (g_profile == "scitokens2") {
182182
profile = SciTokenProfile::SCITOKENS_2_0;
183+
} else if (g_profile == "atjwt") {
184+
profile = SciTokenProfile::AT_JWT;
183185
} else {
184186
fprintf(stderr, "Unknown token profile: %s\n", g_profile.c_str());
185187
return 1;

src/scitokens.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ Acl;
2525
* - COMPAT mode (default) indicates any supported token format
2626
* is acceptable. Where possible, the scope names are translated into
2727
* equivalent SciTokens 1.0 claim names (i.e., storage.read -> read; storage.write -> write).
28-
* - SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0: only accept these specific profiles.
28+
* If a typ header claim is present, use that to deduce type (RFC8725 Section 3.11).
29+
* - SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0, AT_JWT: only accept these specific profiles.
2930
* No automatic translation is performed.
3031
*/
3132
typedef enum _profile {
3233
COMPAT = 0,
3334
SCITOKENS_1_0,
3435
SCITOKENS_2_0,
35-
WLCG_1_0
36+
WLCG_1_0,
37+
AT_JWT
3638
} SciTokenProfile;
3739

3840
SciTokenKey scitoken_key_create(const char *key_id, const char *algorithm, const char *public_contents, const char *private_contents, char **err_msg);

src/scitokens_internal.h

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ friend class scitokens::Validator;
116116
COMPAT = 0,
117117
SCITOKENS_1_0,
118118
SCITOKENS_2_0,
119-
WLCG_1_0
119+
WLCG_1_0,
120+
AT_JWT
120121
};
121122

122123
SciToken(SciTokenKey &signing_algorithm)
@@ -198,6 +199,9 @@ friend class scitokens::Validator;
198199
builder.set_issued_at(time);
199200
builder.set_not_before(time);
200201
builder.set_expires_at(time + std::chrono::seconds(m_lifetime));
202+
if (m_serialize_profile == Profile::AT_JWT) {
203+
builder.set_type("at+jwt");
204+
}
201205

202206
uuid_t uuid;
203207
uuid_generate(uuid);
@@ -258,11 +262,31 @@ class Validator {
258262
}
259263

260264
void verify(const jwt::decoded_jwt &jwt) {
265+
// If token has a typ header claim (RFC8725 Section 3.11), trust that in COMPAT mode.
266+
if (jwt.has_type()) {
267+
std::string t_type = jwt.get_type();
268+
if (m_validate_profile == SciToken::Profile::COMPAT) {
269+
if (t_type == "at+jwt" || t_type == "application/at+jwt") {
270+
m_profile = SciToken::Profile::AT_JWT;
271+
}
272+
} else if (m_validate_profile == SciToken::Profile::AT_JWT) {
273+
if (t_type != "at+jwt" && t_type != "application/at+jwt") {
274+
throw jwt::token_verification_exception("'typ' header claim must be at+jwt");
275+
}
276+
m_profile = SciToken::Profile::AT_JWT;
277+
}
278+
} else {
279+
if (m_validate_profile == SciToken::Profile::AT_JWT) {
280+
throw jwt::token_verification_exception("'typ' header claim must be set for at+jwt tokens");
281+
}
282+
}
261283
if (!jwt.has_payload_claim("iat")) {
262284
throw jwt::token_verification_exception("'iat' claim is mandatory");
263285
}
264-
if (!jwt.has_payload_claim("nbf")) {
265-
throw jwt::token_verification_exception("'nbf' claim is mandatory");
286+
if (m_profile != SciToken::Profile::AT_JWT) {
287+
if (!jwt.has_payload_claim("nbf")) {
288+
throw jwt::token_verification_exception("'nbf' claim is mandatory");
289+
}
266290
}
267291
if (!jwt.has_payload_claim("exp")) {
268292
throw jwt::token_verification_exception("'exp' claim is mandatory");
@@ -360,6 +384,9 @@ class Validator {
360384
if (!jwt.has_payload_claim("aud")) {
361385
throw jwt::token_verification_exception("Malformed token: 'aud' claim required for WLCG profile");
362386
}
387+
} else if (m_profile == SciToken::Profile::AT_JWT) {
388+
// detected early above from typ header claim.
389+
must_verify_everything = false;
363390
} else {
364391
if ((m_validate_profile != SciToken::Profile::COMPAT) &&
365392
(m_validate_profile != SciToken::Profile::SCITOKENS_1_0))

src/verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const char usage[] = \
1616
" -c | --cred <cred_file> File containing the signing credential.\n"
1717
" -i | --issuer <issuer> Issuer of the token to verify.\n"
1818
" -K | --keyid <kid> Name of the token key.\n"
19-
" -p | --profile <profile> Profile to enforce (wlcg, scitokens1, scitokens2).\n"
19+
" -p | --profile <profile> Profile to enforce (wlcg, scitokens1, scitokens2, atjwt).\n"
2020
"\n";
2121

2222
const struct option long_options[] =

0 commit comments

Comments
 (0)