Skip to content
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

Fallback to iframe method when getTokenSilently is given a specific audience #414

Merged
merged 2 commits into from
Apr 15, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions __tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1574,6 +1574,26 @@ describe('Auth0', () => {
},
});
});

it('falls back to the iframe method when an audience is specified', async () => {
const utils = require('../src/utils');
utils.getUniqueScopes.mockReturnValue('offline_access');

const { auth0 } = await setup({
useRefreshTokens: true,
});

await auth0.getTokenSilently({
audience: 'other-audience',
ignoreCache: true,
});

expect(utils.runIframe).toHaveBeenCalledWith(
`https://test.auth0.com/authorize?${TEST_QUERY_PARAMS}${TEST_TELEMETRY_QUERY_STRING}`,
'https://test.auth0.com',
undefined
);
});
});
});

10 changes: 7 additions & 3 deletions src/Auth0Client.ts
Original file line number Diff line number Diff line change
@@ -497,9 +497,13 @@ export default class Auth0Client {

await lock.acquireLock(GET_TOKEN_SILENTLY_LOCK_KEY, 5000);

const authResult = this.options.useRefreshTokens
? await this._getTokenUsingRefreshToken(getTokenOptions)
: await this._getTokenFromIFrame(getTokenOptions);
// Only get an access token using a refresh token if:
// * refresh tokens are enabled
// * no audience has been specified to getTokenSilently (we can only get a token for a new audience when using an iframe)
const authResult =
this.options.useRefreshTokens && !options.audience
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the aud claim of the existing token, so we could see if options.audience is different (presumably, specifying an audience that is the same as the existing one will work for the refresh grant?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we can check that. We only have the aud property from the ID token which will be set to the client ID. The access token may have the correct audience but we can't decode those on the client anyway.

If we could do that check, then yes we could skip this fallback but I can't think of a value to check against that would be reliable enough.

? await this._getTokenUsingRefreshToken(getTokenOptions)
: await this._getTokenFromIFrame(getTokenOptions);

this.cache.save({ client_id: this.options.client_id, ...authResult });

114 changes: 63 additions & 51 deletions static/index.html
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@

<body>
<div id="app" class="container">
<div style="visibility: hidden">
<div style="visibility: hidden;">
<span id="loaded">loaded</span>
</div>

@@ -104,7 +104,13 @@ <h3>Last error</h3>
<div class="card-header">Access Tokens</div>
<div class="card-body">
<ul v-for="token in access_tokens">
<li data-cy="access-token">{{token}}</li>
<li data-cy="access-token">
{{token}} (<a
:href="`https://jwt.io?token=${token}`"
target="_blank"
>view</a
>)
</li>
</ul>
</div>
</div>
@@ -113,7 +119,13 @@ <h3>Last error</h3>
<div class="card-header">
ID Token
</div>
<div class="card-body">{{ id_token }}</div>
<div class="card-body">
{{ id_token }} (<a
:href="`https://jwt.io?token=${id_token}`"
target="_blank"
>view</a
>)
</div>
</div>
</div>

@@ -231,7 +243,7 @@ <h3>Last error</h3>

var app = new Vue({
el: '#app',
data: function() {
data: function () {
var savedData = localStorage.getItem('spa-playground-data');

var data = savedData ? JSON.parse(savedData) : {};
@@ -250,39 +262,39 @@ <h3>Last error</h3>
domain: data.domain || defaultDomain,
clientId: data.clientId || defaultClientId,
audience: data.audience || defaultAudience,
error: null
error: null,
};
},
created: function() {
created: function () {
this.initializeClient();
},
watch: {
useLocalStorage: function() {
useLocalStorage: function () {
this.initializeClient();
this.saveForm();
},
useRefreshTokens: function() {
useRefreshTokens: function () {
this.initializeClient();
this.saveForm();
},
useCache: function() {
useCache: function () {
this.saveForm();
},
useConstructor: function() {
useConstructor: function () {
this.initializeClient();
this.saveForm();
}
},
},
methods: {
initializeClient: function() {
initializeClient: function () {
var _self = this;

const _init = function(auth0) {
const _init = function (auth0) {
_self.auth0 = auth0;
window.auth0 = auth0; // Cypress integration tests support
_self.loading = false;

auth0.isAuthenticated().then(function(isAuthenticated) {
auth0.isAuthenticated().then(function (isAuthenticated) {
_self.isAuthenticated = isAuthenticated;
});
};
@@ -298,7 +310,7 @@ <h3>Last error</h3>
? 'localstorage'
: 'memory',
useRefreshTokens: _self.useRefreshTokens,
audience: _self.audience
audience: _self.audience,
})
);
} else {
@@ -311,11 +323,11 @@ <h3>Last error</h3>
? 'localstorage'
: 'memory',
useRefreshTokens: _self.useRefreshTokens,
audience: _self.audience
audience: _self.audience,
}).then(_init);
}
},
saveForm: function() {
saveForm: function () {
localStorage.setItem(
'spa-playground-data',
JSON.stringify({
@@ -325,11 +337,11 @@ <h3>Last error</h3>
useRefreshTokens: this.useRefreshTokens,
useConstructor: this.useConstructor,
useCache: this.useCache,
audience: this.audience
audience: this.audience,
})
);
},
resetForm: function() {
resetForm: function () {
this.domain = defaultDomain;
this.clientId = defaultClientId;
this.useLocalStorage = false;
@@ -339,71 +351,71 @@ <h3>Last error</h3>
this.useConstructor = false;
this.saveForm();
},
showAuth0Info: function() {
showAuth0Info: function () {
var _self = this;
_self.access_tokens = [];

_self.auth0.getTokenSilently().then(function(token) {
_self.auth0.getTokenSilently().then(function (token) {
_self.access_tokens.push(token);
});

_self.auth0.getUser().then(function(user) {
_self.auth0.getUser().then(function (user) {
_self.profile = user;
});

_self.auth0.getIdTokenClaims().then(function(claims) {
_self.auth0.getIdTokenClaims().then(function (claims) {
_self.id_token = claims.__raw;
});
},
loginPopup: function() {
loginPopup: function () {
var _self = this;

_self.auth0
.loginWithPopup({
redirect_uri: 'http://localhost:3000/callback.html'
redirect_uri: 'http://localhost:3000/callback.html',
})
.then(function() {
auth0.isAuthenticated().then(function(isAuthenticated) {
.then(function () {
auth0.isAuthenticated().then(function (isAuthenticated) {
_self.isAuthenticated = isAuthenticated;
_self.showAuth0Info();
});
});
},
loginRedirect: function() {
loginRedirect: function () {
this.auth0.loginWithRedirect({
redirect_uri: 'http://localhost:3000/'
redirect_uri: 'http://localhost:3000/',
});
},
loginHandleRedirectCallback: function() {
loginHandleRedirectCallback: function () {
var _self = this;

_self.auth0.handleRedirectCallback().then(function() {
_self.auth0.handleRedirectCallback().then(function () {
window.history.replaceState(
{},
document.title,
window.location.origin + '/'
);

auth0.isAuthenticated().then(function(isAuthenticated) {
auth0.isAuthenticated().then(function (isAuthenticated) {
_self.isAuthenticated = isAuthenticated;
_self.showAuth0Info();
});
});
},
getToken: function() {
getToken: function () {
var _self = this;

_self.auth0
.getTokenSilently({ ignoreCache: !_self.useCache })
.then(function(token) {
.then(function (token) {
_self.access_tokens.push(token);
_self.error = null;

auth0.isAuthenticated().then(function(isAuthenticated) {
auth0.isAuthenticated().then(function (isAuthenticated) {
_self.isAuthenticated = isAuthenticated;
});
})
.catch(function(e) {
.catch(function (e) {
console.error(e);

if (e.message) {
@@ -413,56 +425,56 @@ <h3>Last error</h3>
}
});
},
getMultipleTokens: function() {
getMultipleTokens: function () {
var _self = this;

Promise.all([
_self.auth0.getTokenSilently({ ignoreCache: true }),
_self.auth0.getTokenSilently({ ignoreCache: true })
]).then(function(tokens) {
_self.auth0.getTokenSilently({ ignoreCache: true }),
]).then(function (tokens) {
_self.access_tokens = [tokens[0], tokens[1]];
});
},
getTokenPopup: function() {
getTokenPopup: function () {
var _self = this;

_self.auth0
.getTokenWithPopup({
audience: 'https://brucke.auth0.com/api/v2/',
scope: 'read:rules'
scope: 'read:rules',
})
.then(function(token) {
.then(function (token) {
_self.access_tokens = [token];
});
},
getTokenAudience: function() {
getTokenAudience: function () {
var _self = this;

var differentAudienceOptions = {
audience: 'https://brucke.auth0.com/api/v2/',
scope: 'read:rules',
redirect_uri: 'http://localhost:3000/callback.html',
ignoreCache: !_self.useCache
ignoreCache: !_self.useCache,
};

_self.auth0
.getTokenSilently(differentAudienceOptions)
.then(function(token) {
.then(function (token) {
_self.access_tokens = [token];
});
},
logout: function() {
logout: function () {
this.auth0.logout({
returnTo: 'http://localhost:3000/'
returnTo: 'http://localhost:3000/',
});
},
logoutNoClient: function() {
logoutNoClient: function () {
this.auth0.logout({
client_id: null,
returnTo: 'http://localhost:3000/'
returnTo: 'http://localhost:3000/',
});
}
}
},
},
});
</script>
</body>