Skip to content

Commit

Permalink
KEYCLOAK-1150
Browse files Browse the repository at this point in the history
'iss' should be URL not just realm name
  • Loading branch information
stianst committed Mar 26, 2015
1 parent d36ee0d commit b727087
Show file tree
Hide file tree
Showing 36 changed files with 1,041 additions and 978 deletions.
Expand Up @@ -50,7 +50,7 @@ public String getIdTokenString() {


public String getRealm() { public String getRealm() {
// Assumption that issuer contains realm name // Assumption that issuer contains realm name
return token.getIssuer(); return token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
} }


// SERIALIZATION // SERIALIZATION
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/java/org/keycloak/RSATokenVerifier.java
Expand Up @@ -12,11 +12,11 @@
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RSATokenVerifier { public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realm) throws VerificationException { public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl) throws VerificationException {
return verifyToken(tokenString, realmKey, realm, true); return verifyToken(tokenString, realmKey, realmUrl, true);
} }


public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realm, boolean checkActive) throws VerificationException { public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive) throws VerificationException {
JWSInput input = null; JWSInput input = null;
try { try {
input = new JWSInput(tokenString); input = new JWSInput(tokenString);
Expand All @@ -35,7 +35,7 @@ public static AccessToken verifyToken(String tokenString, PublicKey realmKey, St
if (user == null) { if (user == null) {
throw new VerificationException("Token user was null."); throw new VerificationException("Token user was null.");
} }
if (!realm.equals(token.getIssuer())) { if (!realmUrl.equals(token.getIssuer())) {
throw new VerificationException("Token audience doesn't match domain."); throw new VerificationException("Token audience doesn't match domain.");


} }
Expand Down
4 changes: 2 additions & 2 deletions core/src/test/java/org/keycloak/RSAVerifierTest.java
Expand Up @@ -72,7 +72,7 @@ public void initTest() {


token = new AccessToken(); token = new AccessToken();
token.subject("CN=Client") token.subject("CN=Client")
.issuer("domain") .issuer("http://localhost:8080/auth/realm")
.addAccess("service").addRole("admin"); .addAccess("service").addRole("admin");
} }


Expand Down Expand Up @@ -102,7 +102,7 @@ public void testSimpleVerification() throws Exception {
} }


private AccessToken verifySkeletonKeyToken(String encoded) throws VerificationException { private AccessToken verifySkeletonKeyToken(String encoded) throws VerificationException {
return RSATokenVerifier.verifyToken(encoded, idpPair.getPublic(), "domain"); return RSATokenVerifier.verifyToken(encoded, idpPair.getPublic(), "http://localhost:8080/auth/realm");
} }


/* /*
Expand Down
2 changes: 1 addition & 1 deletion core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
Expand Up @@ -104,7 +104,7 @@ public void testSerialization() throws Exception {
private AccessToken createSimpleToken() { private AccessToken createSimpleToken() {
AccessToken token = new AccessToken(); AccessToken token = new AccessToken();
token.id("111"); token.id("111");
token.issuer("acme"); token.issuer("http://localhost:8080/auth/acme");
token.addAccess("foo").addRole("admin"); token.addAccess("foo").addRole("admin");
token.addAccess("bar").addRole("user"); token.addAccess("bar").addRole("user");
return token; return token;
Expand Down
24 changes: 24 additions & 0 deletions docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
Expand Up @@ -79,6 +79,30 @@


<section> <section>
<title>Version specific migration</title> <title>Version specific migration</title>
<section>
<title>Migrating from 1.1.0.Final to 1.2.0.Beta1</title>
<simplesect>
<title><literal>iss</literal> in access and id tokens</title>
<para>
The value of <literal>iss</literal> claim in access and id tokens have changed from <literal>realm name</literal>
to <literal>realm url</literal>. This is required by OpenID Connect specification. If you're using our adapters
there's no change required, other than if you've been using bearer-only without specifying <literal>auth-server-url</literal>
you have to add it now. If you're using another library (or RSATokenVerifier) you need to make the corresponding
changes when verifying <literal>iss</literal>.
</para>
</simplesect>
<simplesect>
<title>OpenID Connect endpoints</title>
<para>
To comply with OpenID Connect specification the authentication and token endpoints have been changed
to having a single authentication endpoint and a single token endpoint. As per-spec <literal>response_type</literal>
and <literal>grant_type</literal> parameters are used to select the required flow. The old endpoints (<literal>/realms/{realm}/protocols/openid-connect/login</literal>,
<literal>/realms/{realm}/protocols/openid-connect/grants/access</literal>, <literal>/realms/{realm}/protocols/openid-connect/refresh</literal>,
<literal>/realms/{realm}/protocols/openid-connect/access/codes)</literal> are now deprecated and will be removed
in a future version.
</para>
</simplesect>
</section>
<section> <section>
<title>Migrating from 1.1.0.Beta2 to 1.1.0.Final</title> <title>Migrating from 1.1.0.Beta2 to 1.1.0.Final</title>
<itemizedlist> <itemizedlist>
Expand Down
Expand Up @@ -2,6 +2,7 @@
"realm" : "demo", "realm" : "demo",
"resource" : "database-service", "resource" : "database-service",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"bearer-only" : true, "bearer-only" : true,
"ssl-required" : "external" "ssl-required" : "external"
} }
Expand Up @@ -52,7 +52,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
PrintWriter writer = resp.getWriter(); PrintWriter writer = resp.getWriter();


writer.write("Realm: "); writer.write("Realm: ");
writer.write(principal.getKeycloakSecurityContext().getIdToken().getIssuer()); writer.write(principal.getKeycloakSecurityContext().getRealm());


writer.write("<br/>User: "); writer.write("<br/>User: ");
writer.write(principal.getKeycloakSecurityContext().getIdToken().getPreferredUsername()); writer.write(principal.getKeycloakSecurityContext().getIdToken().getPreferredUsername());
Expand Down
Expand Up @@ -64,7 +64,7 @@ public AuthOutcome authenticate(HttpFacade exchange) {


protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) { protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
try { try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm()); token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
} catch (VerificationException e) { } catch (VerificationException e) {
log.error("Failed to verify token", e); log.error("Failed to verify token", e);
challenge = challengeResponse(exchange, "invalid_token", e.getMessage()); challenge = challengeResponse(exchange, "invalid_token", e.getMessage());
Expand Down
Expand Up @@ -54,7 +54,7 @@ public static KeycloakPrincipal<RefreshableKeycloakSecurityContext> getPrincipal


try { try {
// Skip check if token is active now. It's supposed to be done later by the caller // Skip check if token is active now. It's supposed to be done later by the caller
AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealm(), false); AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false);
IDToken idToken; IDToken idToken;
if (idTokenString != null && idTokenString.length() > 0) { if (idTokenString != null && idTokenString.length() > 0) {
JWSInput input = new JWSInput(idTokenString); JWSInput input = new JWSInput(idTokenString);
Expand Down
Expand Up @@ -311,7 +311,7 @@ protected AuthChallenge resolveCode(String code) {
refreshToken = tokenResponse.getRefreshToken(); refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken(); idTokenString = tokenResponse.getIdToken();
try { try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm()); token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
if (idTokenString != null) { if (idTokenString != null) {
JWSInput input = new JWSInput(idTokenString); JWSInput input = new JWSInput(idTokenString);
try { try {
Expand Down
Expand Up @@ -107,7 +107,7 @@ public boolean refreshExpiredToken(boolean checkActive) {
String tokenString = response.getToken(); String tokenString = response.getToken();
AccessToken token = null; AccessToken token = null;
try { try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm()); token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
log.debug("Token Verification succeeded!"); log.debug("Token Verification succeeded!");
} catch (VerificationException e) { } catch (VerificationException e) {
log.error("failed verification of token"); log.error("failed verification of token");
Expand Down
Expand Up @@ -173,7 +173,7 @@ public boolean logout() throws LoginException {




protected Auth bearerAuth(String tokenString) throws VerificationException { protected Auth bearerAuth(String tokenString) throws VerificationException {
AccessToken token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm()); AccessToken token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());


boolean verifyCaller; boolean verifyCaller;
if (deployment.isUseResourceRoleMappings()) { if (deployment.isUseResourceRoleMappings()) {
Expand Down
Expand Up @@ -193,7 +193,7 @@ private void parseAccessToken(AccessTokenResponse tokenResponse) throws Verifica
refreshToken = tokenResponse.getRefreshToken(); refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken(); idTokenString = tokenResponse.getIdToken();


token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm()); token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
if (idTokenString != null) { if (idTokenString != null) {
JWSInput input = new JWSInput(idTokenString); JWSInput input = new JWSInput(idTokenString);
try { try {
Expand Down
Expand Up @@ -59,6 +59,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
public static final String PROMPT_PARAM = "prompt"; public static final String PROMPT_PARAM = "prompt";
public static final String LOGIN_HINT_PARAM = "login_hint"; public static final String LOGIN_HINT_PARAM = "login_hint";
public static final String LOGOUT_REDIRECT_URI = "OIDC_LOGOUT_REDIRECT_URI"; public static final String LOGOUT_REDIRECT_URI = "OIDC_LOGOUT_REDIRECT_URI";
public static final String ISSUER = "iss";


private static final Logger log = Logger.getLogger(OIDCLoginProtocol.class); private static final Logger log = Logger.getLogger(OIDCLoginProtocol.class);


Expand Down
Expand Up @@ -314,7 +314,7 @@ protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel
token.audience(client.getClientId()); token.audience(client.getClientId());
token.issuedNow(); token.issuedNow();
token.issuedFor(client.getClientId()); token.issuedFor(client.getClientId());
token.issuer(realm.getName()); token.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER));
if (session != null) { if (session != null) {
token.setSessionState(session.getId()); token.setSessionState(session.getId());
} }
Expand Down
Expand Up @@ -223,6 +223,7 @@ private void createClientSession() {
clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret()); clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, responseType); clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, responseType);
clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUriParam); clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUriParam);
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));


if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state); if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
if (scope != null) clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope); if (scope != null) clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
Expand Down
Expand Up @@ -29,6 +29,7 @@
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.flows.Urls;


import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.OPTIONS; import javax.ws.rs.OPTIONS;
Expand Down Expand Up @@ -319,6 +320,7 @@ public Response buildResourceOwnerPasswordCredentialsGrant() {


ClientSessionModel clientSession = sessions.createClientSession(realm, client); ClientSessionModel clientSession = sessions.createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL); clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));


TokenManager.attachClientSession(userSession, clientSession); TokenManager.attachClientSession(userSession, clientSession);


Expand Down
Expand Up @@ -39,6 +39,7 @@
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.EventsManager; import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.flows.Urls;


import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
Expand All @@ -52,6 +53,7 @@
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;


Expand All @@ -66,6 +68,9 @@ public class UserInfoEndpoint {
@Context @Context
private HttpResponse response; private HttpResponse response;


@Context
private UriInfo uriInfo;

@Context @Context
private KeycloakSession session; private KeycloakSession session;


Expand Down Expand Up @@ -114,7 +119,7 @@ private Response issueUserInfo(String tokenString) {


AccessToken token = null; AccessToken token = null;
try { try {
token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName()); token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
} catch (Exception e) { } catch (Exception e) {
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Token invalid", Status.FORBIDDEN); throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Token invalid", Status.FORBIDDEN);
} }
Expand Down
Expand Up @@ -15,6 +15,7 @@
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.flows.Urls;


import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
Expand Down Expand Up @@ -68,7 +69,7 @@ public Response validateAccessToken(@QueryParam("access_token") String tokenStri
event.event(EventType.VALIDATE_ACCESS_TOKEN); event.event(EventType.VALIDATE_ACCESS_TOKEN);
AccessToken token = null; AccessToken token = null;
try { try {
token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName()); token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
} catch (Exception e) { } catch (Exception e) {
Map<String, String> err = new HashMap<String, String>(); Map<String, String> err = new HashMap<String, String>();
err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT); err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT);
Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.util.CookieHelper; import org.keycloak.services.util.CookieHelper;
import org.keycloak.services.validation.Validation; import org.keycloak.services.validation.Validation;
import org.keycloak.util.Time; import org.keycloak.util.Time;
Expand Down Expand Up @@ -192,12 +193,12 @@ public static Response finishBrowserLogout(KeycloakSession session, RealmModel r
} }




public static AccessToken createIdentityToken(RealmModel realm, UserModel user, UserSessionModel session) { public static AccessToken createIdentityToken(RealmModel realm, UserModel user, UserSessionModel session, String issuer) {
AccessToken token = new AccessToken(); AccessToken token = new AccessToken();
token.id(KeycloakModelUtils.generateId()); token.id(KeycloakModelUtils.generateId());
token.issuedNow(); token.issuedNow();
token.subject(user.getId()); token.subject(user.getId());
token.issuer(realm.getName()); token.issuer(issuer);
if (session != null) { if (session != null) {
token.setSessionState(session.getId()); token.setSessionState(session.getId());
} }
Expand All @@ -209,7 +210,8 @@ public static AccessToken createIdentityToken(RealmModel realm, UserModel user,


public static void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) { public static void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) {
String cookiePath = getIdentityCookiePath(realm, uriInfo); String cookiePath = getIdentityCookiePath(realm, uriInfo);
AccessToken identityToken = createIdentityToken(realm, user, session); String issuer = Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName());
AccessToken identityToken = createIdentityToken(realm, user, session, issuer);
String encoded = encodeToken(realm, identityToken); String encoded = encodeToken(realm, identityToken);
boolean secureOnly = realm.getSslRequired().isRequired(connection); boolean secureOnly = realm.getSslRequired().isRequired(connection);
int maxAge = NewCookie.DEFAULT_MAX_AGE; int maxAge = NewCookie.DEFAULT_MAX_AGE;
Expand Down Expand Up @@ -443,7 +445,7 @@ protected static void isEmailVerificationRequired(RealmModel realm, UserModel us


protected AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString, HttpHeaders headers) { protected AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString, HttpHeaders headers) {
try { try {
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName(), checkActive); AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), checkActive);
if (checkActive) { if (checkActive) {
if (!token.isActive() || token.getIssuedAt() < realm.getNotBefore()) { if (!token.isActive() || token.getIssuedAt() < realm.getNotBefore()) {
logger.debug("identity cookie expired"); logger.debug("identity cookie expired");
Expand Down
Expand Up @@ -140,7 +140,7 @@ protected AdminAuth authenticateRealmAdminRequest(HttpHeaders headers) {
} catch (IOException e) { } catch (IOException e) {
throw new UnauthorizedException("Bearer token format error"); throw new UnauthorizedException("Bearer token format error");
} }
String realmName = token.getIssuer(); String realmName = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.getRealmByName(realmName); RealmModel realm = realmManager.getRealmByName(realmName);
if (realm == null) { if (realm == null) {
Expand Down
Expand Up @@ -158,6 +158,10 @@ public static UriBuilder loginUsernameReminderBuilder(URI baseUri) {
return requiredActionsBase(baseUri).path(LoginActionsService.class, "usernameReminder"); return requiredActionsBase(baseUri).path(LoginActionsService.class, "usernameReminder");
} }


public static String realmIssuer(URI baseUri, String realmId) {
return realmBase(baseUri).path("{realm}").build(realmId).toString();
}

private static UriBuilder realmBase(URI baseUri) { private static UriBuilder realmBase(URI baseUri) {
return UriBuilder.fromUri(baseUri).path(RealmsResource.class); return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
} }
Expand Down
Expand Up @@ -225,7 +225,7 @@ else if (clientId != null) {


public AccessToken verifyToken(String token) { public AccessToken verifyToken(String token) {
try { try {
return RSATokenVerifier.verifyToken(token, realmPublicKey, realm); return RSATokenVerifier.verifyToken(token, realmPublicKey, baseUrl + "/realms/" + realm);
} catch (VerificationException e) { } catch (VerificationException e) {
throw new RuntimeException("Failed to verify token", e); throw new RuntimeException("Failed to verify token", e);
} }
Expand Down

0 comments on commit b727087

Please sign in to comment.