-
Notifications
You must be signed in to change notification settings - Fork 150
Retain access token claim
Madhumita Subramaniam edited this page Aug 1, 2023
·
10 revisions
Reference - https://support.gluu.org/access-management/9401/access-token-introspection-script-how-to-retain-claim/
Create client :
- with access token as jwt (
accessTokenAsJwt=true
) - invoke introspection script during access token creation (
runIntrospectionScriptBeforeAccessTokenAsJwtCreationAndIncludeClaims=true
) - Add introspection script and add it to client (otherwise it will not be invoked)
Client LDIF
dn: inum=0008-525a95a3-5fe1-4ecf-878c-06f438e3f500,ou=clients,o=gluu
objectClass: oxAuthClient
objectClass: top
del: false
displayName: Retain Claims Client
inum: 0008-525a95a3-5fe1-4ecf-878c-06f438e3f500
oxAccessTokenAsJwt: true
oxAttributes:: ew0KICAidGxzQ2xpZW50QXV0aFN1YmplY3REbiI6IG51bGwsDQogICJydW5Jb
nRyb3NwZWN0aW9uU2NyaXB0QmVmb3JlQWNjZXNzVG9rZW5Bc0p3dENyZWF0aW9uQW5kSW5jbHVk
ZUNsYWltcyI6IHRydWUsDQogICJrZWVwQ2xpZW50QXV0aG9yaXphdGlvbkFmdGVyRXhwaXJhdGl
vbiI6IGZhbHNlLA0KICAiYWxsb3dTcG9udGFuZW91c1Njb3BlcyI6IGZhbHNlLA0KICAic3Bvbn
RhbmVvdXNTY29wZXMiOiBbDQogICAgDQogIF0sDQogICJzcG9udGFuZW91c1Njb3BlU2NyaXB0R
G5zIjogWw0KICAgIA0KICBdLA0KICAiYmFja2NoYW5uZWxMb2dvdXRVcmkiOiBbDQogICAgDQog
IF0sDQogICJiYWNrY2hhbm5lbExvZ291dFNlc3Npb25SZXF1aXJlZCI6IGZhbHNlLA0KICAiYWR
kaXRpb25hbEF1ZGllbmNlIjogWw0KICAgIA0KICBdLA0KICAicG9zdEF1dGhuU2NyaXB0cyI6IF
sNCiAgICAiaW51bT1BNTFELkY3MkEsb3U9c2NyaXB0cyxvPWdsdXUiDQogIF0sDQogICJjb25zZ
W50R2F0aGVyaW5nU2NyaXB0cyI6IFsNCiAgICANCiAgXSwNCiAgImludHJvc3BlY3Rpb25TY3Jp
cHRzIjogWw0KICAgICAiaW51bT0yREFGLUFBOTAsb3U9c2NyaXB0cyxvPWdsdXUiDQogIF0sDQo
gICJycHRDbGFpbXNTY3JpcHRzIjogWw0KICAgIA0KICBdDQp9
oxAuthAppType: web
oxAuthBackchannelUserCodeParameter: false
oxAuthClientSecret: dApfwy7qGfBIGHf8802DoQ==
oxAuthGrantType: authorization_code
oxAuthGrantType: client_credentials
oxAuthGrantType: implicit
oxAuthGrantType: refresh_token
oxAuthIdTokenSignedResponseAlg: HS256
oxAuthLogoutSessionRequired: false
oxAuthLogoutURI: https://ce-dev5.gluu.org/identity/ssologout.htm
oxAuthPostLogoutRedirectURI: https://ce-dev5.gluu.org/identity/finishlogout.
htm
oxAuthRedirectURI: https://ce-dev5.gluu.org/identity/authcode.htm
oxAuthRedirectURI: https://ce-dev5.gluu.org/identity/scim/auth
oxAuthRedirectURI: https://ce-dev5.gluu.org/oxauth-rp/home.htm
oxAuthRedirectURI: https://client.example.com/cb
oxAuthRequireAuthTime: false
oxAuthResponseType: code
oxAuthResponseType: id_token
oxAuthScope: inum=10B2,ou=scopes,o=gluu
oxAuthScope: inum=6D99,ou=scopes,o=gluu
oxAuthScope: inum=764C,ou=scopes,o=gluu
oxAuthScope: inum=F0C4,ou=scopes,o=gluu
oxAuthSubjectType: public
oxAuthTokenEndpointAuthMethod: client_secret_basic
oxAuthTrustedClient: false
oxClaimRedirectURI: https://ce-dev5.gluu.org/oxauth/restv1/uma/gather_claims
oxDisabled: false
oxIncludeClaimsInIdToken: false
oxLastAccessTime: 20201125150523.157Z
oxLastLogonTime: 20201125150523.157Z
oxPersistClientAuthorizations: false
oxRptAsJwt: false
Introspection script
Allows to persist data with refresh token and then pass it to access token during refreshing.
# oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
# Copyright (c) 2021, Gluu
#
# Author: Yuriy Zabrovarnyy
#
#
from org.gluu.model.custom.script.type.introspection import IntrospectionType
from java.lang import String
from org.gluu.oxauth.model.common import AuthorizationGrantList
from org.gluu.service.cdi.util import CdiUtil
from org.gluu.oxauth.service import GrantService
from org.gluu.oxauth.model.ldap import TokenType
from org.gluu.oxauth.model.ldap import TokenLdap
class Introspection(IntrospectionType):
def __init__(self, currentTimeMillis):
self.currentTimeMillis = currentTimeMillis
def init(self, configurationAttributes):
print "Introspection script (retain claims). Initializing ..."
print "Introspection script (retain claims). Initialized successfully"
return True
def destroy(self, configurationAttributes):
print "Introspection script (retain claims). Destroying ..."
print "Introspection script (retain claims). Destroyed successfully"
return True
def getApiVersion(self):
return 1
# Returns boolean, true - apply introspection method, false - ignore it.
# This method is called after introspection response is ready. This method can modify introspection response.
# Note :
# responseAsJsonObject - is org.codehaus.jettison.json.JSONObject, you can use any method to manipulate json
# context is reference of org.gluu.oxauth.service.external.context.ExternalIntrospectionContext (in https://github.com/GluuFederation/oxauth project, )
def modifyResponse(self, responseAsJsonObject, context):
print "modifyResponse invoked"
authorizationGrantList = CdiUtil.bean(AuthorizationGrantList)
grantService = CdiUtil.bean(GrantService)
refreshToken = context.getHttpRequest().getParameter("refresh_token")
if refreshToken is None:
print "No refresh token parameter. Put original claim - claim1=value1"
responseAsJsonObject.accumulate("claim1", "value1") # AT1
# save it also in refresh token
grants = grantService.getGrantsByGrantId(context.getTokenGrant().getGrantId())
RT1 = {}
for grant in grants:
if (grant.getTokenTypeEnum() == TokenType.REFRESH_TOKEN):
RT1 = grant
print "RT1 hashed code: " + RT1.getTokenCode()
RT1.getAttributes().getAttributes().put("claim1", "value1")
grantService.mergeSilently(RT1)
return True
responseAsJsonObject.accumulate("refresh_token", refreshToken)
print "Refresh token: " + refreshToken
clientId = context.getTokenGrant().getClientId()
print "ClientId: " + clientId
grantId = authorizationGrantList.getAuthorizationGrantByRefreshToken(clientId, refreshToken).getGrantId()
print "grantId: " + grantId
responseAsJsonObject.accumulate("grant_id", grantId)
grants = grantService.getGrantsByGrantId(grantId)
RT = {}
for grant in grants:
if (grant.getTokenTypeEnum() == TokenType.REFRESH_TOKEN):
RT = grant
print "RT hashed code: " + RT.getTokenCode()
valueFromAT = RT.getAttributes().getAttributes().get("claim1")
print "valueFromAT: " + valueFromAT
responseAsJsonObject.accumulate("claim1", valueFromAT)
return True
/opt/gluu/jetty/oxauth/logs/oxauth_script.log
(PythonService.java:243) - modifyResponse invoked
(PythonService.java:243) - No refresh token parameter. Put original claim - claim1=value1
(PythonService.java:243) - RT1 hashed code: 3e5aa58c15cf5c382dce9033c0a2d9e1164dc4bbaf557c4cf512f26ddfba321e
(PythonService.java:243) - modifyResponse invoked
(PythonService.java:243) - Refresh token: 5a80dffa-f96b-4b62-8f5b-c933b10cf513
(PythonService.java:243) - ClientId: 0008-525a95a3-5fe1-4ecf-878c-06f438e3f500
(PythonService.java:243) - grantId: 97650fac-aa45-4068-a35b-bf5aee261d57
(PythonService.java:243) - RT hashed code: 3e5aa58c15cf5c382dce9033c0a2d9e1164dc4bbaf557c4cf512f26ddfba321e
(PythonService.java:243) - valueFromAT: value1
Client test
@Parameters({"userId", "userSecret", "redirectUri"})
@Test(enabled = false) // retain claims script has to be enabled and client pre-configured (not avaiable in test suite)
public void retainClaimAuthorizationCodeFlow(final String userId, final String userSecret, final String redirectUri) throws Exception {
showTitle("authorizationCodeFlow");
List<ResponseType> responseTypes = Arrays.asList(
ResponseType.CODE,
ResponseType.ID_TOKEN);
List<String> scopes = Arrays.asList("openid", "profile", "address", "email", "phone", "user_name");
String clientId = "0008-525a95a3-5fe1-4ecf-878c-06f438e3f500";
String clientSecret = "V9RKUZOtfk92";//registerResponse.getClientSecret();
// 2. Request authorization and receive the authorization code.
String nonce = UUID.randomUUID().toString();
AuthorizationResponse authorizationResponse = requestAuthorization(userId, userSecret, redirectUri, responseTypes, scopes, clientId, nonce);
String scope = authorizationResponse.getScope();
String authorizationCode = authorizationResponse.getCode();
String idToken = authorizationResponse.getIdToken();
// 3. Request access token using the authorization code.
TokenRequest tokenRequest = new TokenRequest(GrantType.AUTHORIZATION_CODE);
tokenRequest.setCode(authorizationCode);
tokenRequest.setRedirectUri(redirectUri);
tokenRequest.setAuthUsername(clientId);
tokenRequest.setAuthPassword(clientSecret);
tokenRequest.setAuthenticationMethod(AuthenticationMethod.CLIENT_SECRET_BASIC);
TokenClient tokenClient1 = newTokenClient(tokenRequest);
tokenClient1.setRequest(tokenRequest);
TokenResponse tokenResponse1 = tokenClient1.exec();
showClient(tokenClient1);
assertEquals(tokenResponse1.getStatus(), 200, "Unexpected response code: " + tokenResponse1.getStatus());
assertNotNull(tokenResponse1.getEntity(), "The entity is null");
assertNotNull(tokenResponse1.getAccessToken(), "The access token is null");
assertNotNull(tokenResponse1.getExpiresIn(), "The expires in value is null");
assertNotNull(tokenResponse1.getTokenType(), "The token type is null");
assertNotNull(tokenResponse1.getRefreshToken(), "The refresh token is null");
String refreshToken = tokenResponse1.getRefreshToken();
// 4. Validate id_token
Jwt jwt = Jwt.parse(idToken);
Asserter.assertIdToken(jwt, JwtClaimName.CODE_HASH);
// 5. Request new access token using the refresh token.
TokenClient tokenClient2 = new TokenClient(tokenEndpoint);
tokenClient2.setExecutor(clientExecutor(true));
TokenResponse tokenResponse2 = tokenClient2.execRefreshToken(scope, refreshToken, clientId, clientSecret);
showClient(tokenClient2);
assertEquals(tokenResponse2.getStatus(), 200, "Unexpected response code: " + tokenResponse2.getStatus());
assertNotNull(tokenResponse2.getEntity(), "The entity is null");
assertNotNull(tokenResponse2.getAccessToken(), "The access token is null");
assertNotNull(tokenResponse2.getTokenType(), "The token type is null");
assertNotNull(tokenResponse2.getRefreshToken(), "The refresh token is null");
assertNotNull(tokenResponse2.getScope(), "The scope is null");
String accessToken = tokenResponse2.getAccessToken();
System.out.println("AT2: " + accessToken);
Jwt at2Jwt = Jwt.parse(accessToken);
System.out.println("AT2 claims: " + at2Jwt.getClaims().toJsonString());
}
Client test output
#######################################################
TEST: authorizationCodeFlow
#######################################################
authenticateResourceOwnerAndGrantAccess: Cleaning cookies
authenticateResourceOwnerAndGrantAccess: authorizationRequestUrl:https://dluu.org/oxauth/restv1/authorize?response_type=code+id_token&client_id=0008-525a95a3-5fe1-4ecf-878c-06f438e3f500&scope=openid+profile+address+email+phone+user_name&redirect_uri=https%3A%2F%2Fdluu.org%2Foxauth-rp%2Fhome.htm&state=b3b84a89-93bf-43b1-b97d-97f967d9171d&nonce=4c7e063f-a7b3-4865-8183-da1d7399a42d
17:15:23.185 [main] ERROR com.gargoylesoftware.htmlunit.javascript.StrictErrorReporter - runtimeError: message=[An invalid or illegal selector was specified (selector: '*,:x' error: Invalid selector: :x).] sourceName=[https://dluu.org/oxauth/js/jquery-3.4.1.min.js] line=[2] lineSource=[null] lineOffset=[0]
authenticateResourceOwnerAndGrantAccess: sessionState:f6eb5c6f93a4e6dfdf961251e207fcd941fa4695fea9cae18fce288231b7caf6.ec2fea2e-c35a-4812-8fbe-d6f454281158
authenticateResourceOwnerAndGrantAccess: sessionId:469f56b5-13fd-45f1-b03f-9132a733aac1
-------------------------------------------------------
REQUEST:
-------------------------------------------------------
https://dluu.org/oxauth/restv1/authorize?response_type=code+id_token&client_id=0008-525a95a3-5fe1-4ecf-878c-06f438e3f500&scope=openid+profile+address+email+phone+user_name&redirect_uri=https%3A%2F%2Fdluu.org%2Foxauth-rp%2Fhome.htm&state=b3b84a89-93bf-43b1-b97d-97f967d9171d&nonce=4c7e063f-a7b3-4865-8183-da1d7399a42d
-------------------------------------------------------
RESPONSE:
-------------------------------------------------------
HTTP/1.1 302 Found
Location: https://dluu.org/oxauth-rp/home.htm#code=b52fa9ea-cfe2-45b7-b88a-040e6b969111&scope=openid+user_name+email&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJCMUYzLUFFQUUtQjc5OCIsImNvZGUiOiI4ZjkxYzcwNy1lODIwLTQwOWEtOGEwZS1lNmMwZjVjYzE2OTYiLCJhbXIiOlsiLTEiXSwiaXNzIjoiaHR0cHM6Ly9jZS1kZXY1LmdsdXUub3JnIiwibm9uY2UiOiI0YzdlMDYzZi1hN2IzLTQ4NjUtODE4My1kYTFkNzM5OWE0MmQiLCJzaWQiOiI4NzAyODQxMC1hMTY1LTRjNjEtOTA4Yy04NmY3ZTNmZWNjM2IiLCJveE9wZW5JRENvbm5lY3RWZXJzaW9uIjoib3BlbmlkY29ubmVjdC0xLjAiLCJhdWQiOiIwMDA4LTUyNWE5NWEzLTVmZTEtNGVjZi04NzhjLTA2ZjQzOGUzZjUwMCIsImFjciI6ImF1dGhfbGRhcF9zZXJ2ZXIiLCJjX2hhc2giOiJNRHp0eFFoRk9obFFXelVDdl9XVzdnIiwic19oYXNoIjoid1VGS0NFWV8xNWo4RFZKUUFsbTVrQSIsImF1dGhfdGltZSI6MTYxNDI2NjQ1MywiZXhwIjoxNjE0MjcwMDU0LCJncmFudCI6ImF1dGhvcml6YXRpb25fY29kZSIsImlhdCI6MTYxNDI2NjQ1NH0.aaeq4ZrOpWcMmcv69CgYaWNpR-DcLtkKUespStWZY_Y&session_id=469f56b5-13fd-45f1-b03f-9132a733aac1&state=b3b84a89-93bf-43b1-b97d-97f967d9171d&session_state=f6eb5c6f93a4e6dfdf961251e207fcd941fa4695fea9cae18fce288231b7caf6.ec2fea2e-c35a-4812-8fbe-d6f454281158&sid=87028410-a165-4c61-908c-86f7e3fecc3b
-------------------------------------------------------
REQUEST:
-------------------------------------------------------
POST /oxauth/restv1/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: dluu
Authorization: Basic MDAwOC01MjVhOTVhMy01ZmUxLTRlY2YtODc4Yy0wNmY0MzhlM2Y1MDA6VjlSS1VaT3Rmazky
grant_type=authorization_code&code=b52fa9ea-cfe2-45b7-b88a-040e6b969111&redirect_uri=https%3A%2F%2Fdluu%2Foxauth-rp%2Fhome.htm
-------------------------------------------------------
RESPONSE:
-------------------------------------------------------
HTTP/1.1 200
Cache-Control: no-store
Connection: Keep-Alive
Content-Length: 1794
Content-Type: application/json
Date: Thu, 25 Feb 2021 15:20:54 GMT
Keep-Alive: timeout=5, max=100
Pragma: no-cache
Server: Apache/2.4.29 (Ubuntu)
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block
{"access_token":"eyJraWQiOiI0MDM5ZjZkYi1hZTM4LTQ5MzQtOGI1ZC1jYTkzNzdmODljNWRfc2lnX3JzMjU2IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJhdWQiOiIwMDA4LTUyNWE5NWEzLTVmZTEtNGVjZi04NzhjLTA2ZjQzOGUzZjUwMCIsInN1YiI6IkIxRjMtQUVBRS1CNzk4IiwieDV0I1MyNTYiOiIiLCJjb2RlIjoiZDFkNmZjY2ItZWUyMS00NmI3LTg0Y2YtMTQ4M2ViZDFkODU3Iiwic2NvcGUiOlsib3BlbmlkIiwidXNlcl9uYW1lIiwiZW1haWwiXSwiaXNzIjoiaHR0cHM6Ly9jZS1kZXY1LmdsdXUub3JnIiwiY2xhaW0xIjoidmFsdWUxIiwidG9rZW5fdHlwZSI6ImJlYXJlciIsImV4cCI6MTYxNDI2Njc1NCwiaWF0IjoxNjE0MjY2NDU0LCJjbGllbnRfaWQiOiIwMDA4LTUyNWE5NWEzLTVmZTEtNGVjZi04NzhjLTA2ZjQzOGUzZjUwMCIsInVzZXJuYW1lIjoib3hBdXRoIFRlc3QgVXNlciJ9.Rm5kc6g1CXt9EL9J-we83iy9_MsNiJjVtWW_0IfkT5UGCGTx7Td6f2pyvurcFTNNyrANP6yI6oYIBK_yqldfQ-zG3YxVBMidPCQo8dcYU9nJlZaq11R6glYEDOrnMrLTu9YM2CciyhtMEfHyhL0p2XYgkpdjbaa3GWt-dmh5bqqAJ2pP-59e2MDCcskrBOFUaIaCy7g2odYn9HUPcKKJB-PVVHUn-oNssE6kVLkT0B9fb75tutf9tT5JHiX4VRdAarfIVPl1JEnjws16UY4mClaQ7NuDM92PqyxVuVaE1EZgXeX26VqAkiawNB0lmwd4oHhCDV7YHoyhFr3H1eCPAg","refresh_token":"5a80dffa-f96b-4b62-8f5b-c933b10cf513","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdF9oYXNoIjoicmJkdE9sdHEyOHIyNlZCZTFXMjhSQSIsInN1YiI6IkIxRjMtQUVBRS1CNzk4IiwiY29kZSI6IjI2OGVlZjY1LTUyNDMtNDQ1Mi05Zjg2LWMyMDhlMjVmNWY3ZCIsImFtciI6WyItMSJdLCJpc3MiOiJodHRwczovL2NlLWRldjUuZ2x1dS5vcmciLCJub25jZSI6IjRjN2UwNjNmLWE3YjMtNDg2NS04MTgzLWRhMWQ3Mzk5YTQyZCIsInNpZCI6Ijg3MDI4NDEwLWExNjUtNGM2MS05MDhjLTg2ZjdlM2ZlY2MzYiIsIm94T3BlbklEQ29ubmVjdFZlcnNpb24iOiJvcGVuaWRjb25uZWN0LTEuMCIsImF1ZCI6IjAwMDgtNTI1YTk1YTMtNWZlMS00ZWNmLTg3OGMtMDZmNDM4ZTNmNTAwIiwiYWNyIjoiYXV0aF9sZGFwX3NlcnZlciIsImNfaGFzaCI6Ik1EenR4UWhGT2hsUVd6VUN2X1dXN2ciLCJhdXRoX3RpbWUiOjE2MTQyNjY0NTMsImV4cCI6MTYxNDI3MDA1NSwiZ3JhbnQiOiJhdXRob3JpemF0aW9uX2NvZGUiLCJpYXQiOjE2MTQyNjY0NTV9.AACxuBXsPgssVVlbzVD-XJCq3KZTdC4wRpUotV5Qsr4","token_type":"bearer","expires_in":299}
-------------------------------------------------------
REQUEST:
-------------------------------------------------------
POST /oxauth/restv1/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: dluu
Authorization: Basic MDAwOC01MjVhOTVhMy01ZmUxLTRlY2YtODc4Yy0wNmY0MzhlM2Y1MDA6VjlSS1VaT3Rmazky
grant_type=refresh_token&scope=openid+user_name+email&refreshToken=5a80dffa-f96b-4b62-8f5b-c933b10cf513
-------------------------------------------------------
RESPONSE:
-------------------------------------------------------
HTTP/1.1 200
Cache-Control: no-store
Connection: Keep-Alive
Content-Length: 1228
Content-Type: application/json
Date: Thu, 25 Feb 2021 15:20:55 GMT
Keep-Alive: timeout=5, max=100
Pragma: no-cache
Server: Apache/2.4.29 (Ubuntu)
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block
{"access_token":"eyJraWQiOiI0MDM5ZjZkYi1hZTM4LTQ5MzQtOGI1ZC1jYTkzNzdmODljNWRfc2lnX3JzMjU2IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJCMUYzLUFFQUUtQjc5OCIsImNvZGUiOiJkODc2OTg3My1hMTIzLTQxOWUtYWE5ZS1kNDU4NzgyYTE2MzkiLCJpc3MiOiJodHRwczovL2NlLWRldjUuZ2x1dS5vcmciLCJ0b2tlbl90eXBlIjoiYmVhcmVyIiwiY2xpZW50X2lkIjoiMDAwOC01MjVhOTVhMy01ZmUxLTRlY2YtODc4Yy0wNmY0MzhlM2Y1MDAiLCJhdWQiOiIwMDA4LTUyNWE5NWEzLTVmZTEtNGVjZi04NzhjLTA2ZjQzOGUzZjUwMCIsIng1dCNTMjU2IjoiIiwicmVmcmVzaF90b2tlbiI6IjVhODBkZmZhLWY5NmItNGI2Mi04ZjViLWM5MzNiMTBjZjUxMyIsInNjb3BlIjpbIm9wZW5pZCIsInVzZXJfbmFtZSIsImVtYWlsIl0sImdyYW50X2lkIjoiOTc2NTBmYWMtYWE0NS00MDY4LWEzNWItYmY1YWVlMjYxZDU3IiwiY2xhaW0xIjoidmFsdWUxIiwiZXhwIjoxNjE0MjY2NzU1LCJpYXQiOjE2MTQyNjY0NTUsInVzZXJuYW1lIjoib3hBdXRoIFRlc3QgVXNlciJ9.uIuzMiPm_PL2rtHH3mwq74EG975928DCUUL2w6WtT_zbgweMYdZSCdxEBuRC5S0tFIX4w9T93AtRasjzZd0a0qxHQY9Qm3_zey6rUoa4NntPNapAwY9qHETvNEkIj8fplt-nttojogZ5l2nqiPXCY1CKt9zLx7_QoCaBfWGZuigD0agI80wQzZ9yJOtVCGgRwr7WdFwB1PasXn_yjhYqpkN3WffY-hv3QtLYKWzXUFGxVQKbjeANei0CjdTBD5MNHcvydJo6ca3g5fo7TwsOlUYlldOLAJzoR3Yh8CupAtLc0SuXvJqXqjdxHVwLI-AqNyjq2eMxLU97sI68Nb46yA","refresh_token":"7ac697ec-6764-4624-b34f-9b33abe3e865","scope":"openid user_name email","token_type":"bearer","expires_in":299}
AT2: eyJraWQiOiI0MDM5ZjZkYi1hZTM4LTQ5MzQtOGI1ZC1jYTkzNzdmODljNWRfc2lnX3JzMjU2IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJCMUYzLUFFQUUtQjc5OCIsImNvZGUiOiJkODc2OTg3My1hMTIzLTQxOWUtYWE5ZS1kNDU4NzgyYTE2MzkiLCJpc3MiOiJodHRwczovL2NlLWRldjUuZ2x1dS5vcmciLCJ0b2tlbl90eXBlIjoiYmVhcmVyIiwiY2xpZW50X2lkIjoiMDAwOC01MjVhOTVhMy01ZmUxLTRlY2YtODc4Yy0wNmY0MzhlM2Y1MDAiLCJhdWQiOiIwMDA4LTUyNWE5NWEzLTVmZTEtNGVjZi04NzhjLTA2ZjQzOGUzZjUwMCIsIng1dCNTMjU2IjoiIiwicmVmcmVzaF90b2tlbiI6IjVhODBkZmZhLWY5NmItNGI2Mi04ZjViLWM5MzNiMTBjZjUxMyIsInNjb3BlIjpbIm9wZW5pZCIsInVzZXJfbmFtZSIsImVtYWlsIl0sImdyYW50X2lkIjoiOTc2NTBmYWMtYWE0NS00MDY4LWEzNWItYmY1YWVlMjYxZDU3IiwiY2xhaW0xIjoidmFsdWUxIiwiZXhwIjoxNjE0MjY2NzU1LCJpYXQiOjE2MTQyNjY0NTUsInVzZXJuYW1lIjoib3hBdXRoIFRlc3QgVXNlciJ9.uIuzMiPm_PL2rtHH3mwq74EG975928DCUUL2w6WtT_zbgweMYdZSCdxEBuRC5S0tFIX4w9T93AtRasjzZd0a0qxHQY9Qm3_zey6rUoa4NntPNapAwY9qHETvNEkIj8fplt-nttojogZ5l2nqiPXCY1CKt9zLx7_QoCaBfWGZuigD0agI80wQzZ9yJOtVCGgRwr7WdFwB1PasXn_yjhYqpkN3WffY-hv3QtLYKWzXUFGxVQKbjeANei0CjdTBD5MNHcvydJo6ca3g5fo7TwsOlUYlldOLAJzoR3Yh8CupAtLc0SuXvJqXqjdxHVwLI-AqNyjq2eMxLU97sI68Nb46yA
AT2 claims: {"sub":"B1F3-AEAE-B798","code":"d8769873-a123-419e-aa9e-d458782a1639","iss":"https://dluu.org","token_type":"bearer","client_id":"0008-525a95a3-5fe1-4ecf-878c-06f438e3f500","aud":"0008-525a95a3-5fe1-4ecf-878c-06f438e3f500","x5t#S256":"","refresh_token":"5a80dffa-f96b-4b62-8f5b-c933b10cf513","scope":["openid","user_name","email"],"grant_id":"97650fac-aa45-4068-a35b-bf5aee261d57","claim1":"value1","exp":1614266755,"iat":1614266455,"username":"oxAuth Test User"}