-
Notifications
You must be signed in to change notification settings - Fork 135
feat: Expose sap_id_type claim via SapIdToken#getIdType #1983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /** | ||
| * SPDX-FileCopyrightText: 2018-2023 SAP SE or an SAP affiliate company and Cloud Security Client Java contributors | ||
| * <p> | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| package com.sap.cloud.security.token; | ||
|
|
||
| import jakarta.annotation.Nullable; | ||
|
|
||
| /** | ||
| * Type of the principal an IAS-issued token belongs to. Mirrors the {@code sap_id_type} JWT claim | ||
| * issued by SAP Cloud Identity Service. | ||
| */ | ||
| public enum SapIdType { | ||
| /** Human end-user principal. */ | ||
| USER("user"), | ||
| /** Technical / application principal. */ | ||
| APP("app"); | ||
|
|
||
| private final String claimValue; | ||
|
|
||
| SapIdType(String claimValue) { | ||
| this.claimValue = claimValue; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the raw claim value used on the wire. | ||
| * | ||
| * @return the raw claim value as it appears in the JWT (e.g. {@code "user"} or {@code "app"}). | ||
| */ | ||
| public String claimValue() { | ||
| return claimValue; | ||
| } | ||
|
|
||
| /** | ||
| * Resolves a {@link SapIdType} from a raw {@code sap_id_type} claim value. | ||
| * | ||
| * @param value the claim value, may be {@code null} | ||
| * @return the matching {@link SapIdType}, or {@code null} if {@code value} is {@code null} or | ||
| * does not match a known type (forward-compatibility for future values) | ||
| */ | ||
| @Nullable | ||
| public static SapIdType fromClaimValue(@Nullable String value) { | ||
| if (value == null) { | ||
| return null; | ||
| } | ||
| for (SapIdType t : values()) { | ||
| if (t.claimValue.equals(value)) { | ||
| return t; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,4 +59,17 @@ public String getIssuer() { | |
| public String getCnfX509Thumbprint() { | ||
| return getAttributeFromClaimAsString(TokenClaims.CNF, TokenClaims.CNF_X5T); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the principal type carried by the {@code sap_id_type} claim. Issued by SAP Cloud | ||
| * Identity Service to disambiguate human end-users ({@link SapIdType#USER}) from | ||
| * technical/application principals ({@link SapIdType#APP}). | ||
| * | ||
| * @return the resolved {@link SapIdType}, or {@code null} if the claim is absent or carries | ||
| * an unknown value (e.g. a future principal type). | ||
| */ | ||
| @Nullable | ||
| public SapIdType getIdType() { | ||
| return SapIdType.fromClaimValue(getClaimAsString(TokenClaims.SAP_ID_TYPE)); | ||
| } | ||
|
Comment on lines
+72
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The enum return type is the cleanest approach but it has the problem that it is not forward-compatible if new claim values are added, e.g. I would go for a This is important when high-lever modules in the library stack like CAP use this feature: they cannot simply set a minimum version of the BTP sec libs for applications and begin to use newer enum values. Instead, they need to work also when older versions of the library are installed and where no enum value exists for new claim values. |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how it is implemented in Node.js (probably like this) but I think it would be best to be conservative and demand
"user"before attempting a token exchange. Theoretically, in this case, there could be new types introduced later for which the extension should also do the exchange but it does not do it because it requires"user". However, this is better than the alternative where the extension checks for"app"and attempts an exchange in all other cases, even when it should not (e.g. when new non-named-user values like"agent"are introduced).