plusauth / oidc-client-js Goto Github PK
View Code? Open in Web Editor NEWOpenID Connect (OIDC) and OAuth2 library for browser-based JavaScript applications.
Home Page: https://plusauth.github.io/oidc-client-js/
License: MIT License
OpenID Connect (OIDC) and OAuth2 library for browser-based JavaScript applications.
Home Page: https://plusauth.github.io/oidc-client-js/
License: MIT License
Hi—thanks for putting this library together. I've been experimenting with it for a few days and it works really well 🙌
One thing that might be a nice addition is throwing a custom OIDCClientError
when the user manually closes the login popup. I believe I've seen this in other OAuth libraries. Is this something you would consider adding?
It seems like this scenario could be tracked or detected in the runPopup
method, similar to the timeoutId
:
oidc-client-js/src/utils/popup.ts
Line 15 in aff27a6
Same issue as in #6 , but this time in silent_login method, where response_mode is hard coded:
} else {
const authUrl = await this.createAuthRequest( {
...finalOptions,
display: 'page',
response_mode: 'query',
prompt: finalOptions.prompt || 'none',
request_type: 's'
}, localState )
By fixing #17, the minor 1.3.0 introduced a breaking change.
We make use of getExpiresAt
to avoid using the timer and comparing the value to now before api calls, could it be restored?
When using issuer https://accounts.google.com, the library fails as Google does not seem to support the content-type header in the openid configuration request.
Error message:
Access to fetch at 'https://accounts.google.com/.well-known/openid-configuration' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
When I use this library with config:
response_mode: 'fragment',
response_type: 'id_token token'
The tokens are returned in a fragment
When I use this library with config:
response_mode: 'fragment',
response_type: 'code'
The tokens are returned in a query
I guess this depends on provider setting, but when I added manually response_mode in query string to authorization endpoint for second configuration, it worked and returned as a fragment.
Current behavior is that during a token request, the client_secret is not being sent unless it's configured in a way that even the code doesn't seem to anticipate.
It is called here:
/**
* Exchange authorization code retrieved from auth request result.
* @param options
* @private
*/
private async exchangeAuthorizationCode( options: TokenRequestOption ){
if ( !this.options.endpoints?.token_endpoint ){
await this.fetchFromIssuer();
}
const { extraTokenHeaders, extraTokenParams, ...rest } = options
const mergedOptions = {
grant_type: 'authorization_code',
client_id: this.options.client_id,
client_secret: this.options.client_secret,
redirect_uri: this.options.redirect_uri,
...rest,
...extraTokenParams || {}
}
for ( const req of ['code', 'redirect_uri', 'code_verifier', 'client_id'] as const ){
if ( !mergedOptions[req] ){
return Promise.reject( new Error( `"${ req }" is required` ) );
}
}
return this.http( {
url: `${ this.options.endpoints!.token_endpoint }`,
method: 'POST',
requestType: 'form',
body: mergedOptions as any,
headers: extraTokenHeaders
} )
}
but due to earlier code, namely in createAuthRequest, which saves authParams to state without client_secret property, and then loginCallback which retrieves the authParams from state, the client_secrets will always get wiped out from the mergedOptions unless they are added to extraParams in the client configuration like so:
const auth= new OIDCClient({
issue: 'foo'
...
silent_redirect_uri: '...',
extraParams: {
client_secret: 'foo_secret'
}
})
This IPlusAuthClientOptions has a property for client_secrets, but due to later (mis)handling of it in the code, it is effectively lost:
export interface IPlusAuthClientOptions extends Omit<AuthRequestOptions, 'request_type'>,
Omit<LogoutRequestOptions, 'localOnly'> {
authStore?: StateStore;
/**
* Enable/disable automatic access token renewal. If enabled, access tokens will be refreshed by using silent
* login in an iframe before they expire.
*
* Also see {@link secondsToRefreshAccessTokenBeforeExp}
*/
autoSilentRenew?: boolean;
/**
* Enable/Disable session monitoring.
*/
checkSession?: boolean;
/**
* Session checking interval in milliseconds. Defaults to 2000
*/
checkSessionInterval?: number;
client_id: string;
client_secret?: string;
}
In 9c8718e, you added getter for expires_at.
My initial request was to be able to calculate expires_at after new tokens retrieval to manually trigger token renewal.
Most IDP provider will provide expires_in, but only a few do provide expires_at, this is why I was calculating it from expires_in.
Would it be therefore possible to have a getter for expires_in as well?
Hello,
I was looking for suitable client OIDC OAuth2 javascript library to my Single page application(SPA). I researched and came accross this plusauth oidc-client library and looks very impressive and promising as it handles more use cases and feature rich for SSO login support. But i have this access token renewal with refresh token api call and faced some issues as i mention in below. I followed steps as per your documentation and saw some shortcomming in handling refresh token. SO i think it's worth bringing as issue and getting it addressed.
Environment: AWS Cognito as OIDC Provider.
Issue 1 - Retention of Refresh Token in OIDCClient:
The OIDCClient is not retaining the existing refresh token after its initial use to refresh the access token. This poses a problem, especially in scenarios where OIDC providers might have refresh token rotation policies that allow reusing the existing refresh token until it expires. In the case of AWS Cognito, which I'm using as the OIDC Provider, there is no provision to return the refresh token along with the renewed access token and ID Token during the refresh process. This results in a disrupted flow after the first refresh because the OIDCClient is not preserving the original refresh token. I researched on stackoverflow and other recomandations that during access token renewal, we will not get back refresh token. so in this api the refresh token is getting overwritten as blank. I was able to resolve it as an example below, there could be other ways too, but the refresh token has to be retained till user logout or it expires.
ex:,
private async handleTokenResult( tokenResult: TokenResponse, authParams: AuthRequestOptions,
finalOptions: IPlusAuthClientOptions ){
...
....
...
if(tokenResult.refresh_token){
return {
authParams,
user,
...tokenResult,
id_token: parsedIDToken,
id_token_raw: tokenResult.id_token,
scope: tokenResult.scope || authParams.scope,
}
}
else{
return {
authParams,
user,
...tokenResult,
refresh_token: this.refreshToken,
id_token: parsedIDToken,
id_token_raw: tokenResult.id_token,
scope: tokenResult.scope || authParams.scope,
}
}
}
Issue 2 - Validation of IDToken in Handle Token Result Method:
The "handleTokenResult" method of client.ts calls the "validateIdToken" of jose.ts to validate the ID Token both during the initial authentication and when refreshing the access token. However, it appears that the validation process is checking for a valid nonce in the ID Token. This nonce is provided by the server during authentication using login credentials such as a username or email and password. Notably, when refreshing the access token using the refresh token (and not the credentials), the server does not include the nonce in the ID Token. Therefore, during the token validation process for refresh, If it can be optional and if not present, it should not validate for a valid nonce since it is not required in this context.
ex:
export function validateIdToken( id_token: string, nonce: string, options: IPlusAuthClientOptions, refreshToken:string | undefined ) {
....
...
if (refreshToken !== undefined && nonce !== jwt.payload.nonce ) {
throw new Error( Invalid nonce in id_token: ${ jwt.payload.nonce }
);
}
.....
}
It's important to mention that I've also passed the nonce in the OIDCClient constructor during initialization. However, the OIDCClient is currently considering the nonce from the server, which may lead to this inconsistency in the validation process. So it could be made optional in this validation process.
I've arrived at these conclusions based on my analysis, research, and debugging. If there are any recommended adjustments or configurations that could be made within the OIDCClient library to address these issues, I would greatly appreciate your guidance on how to overcome them. Furthermore, if these observations are indeed indicative of bugs or shortcoming within the library, I kindly request your assistance in resolving them so that it may help more user community like me.
Thank you
Since updating "@plusauth/oidc-client-js" to 1.2.4 I keep getting the following error when using the OIDCClient.
Uncaught (in promise) TypeError: this.stateStore.clear is not a function
Anyone else seeing issues like this?
expires_at or expires_in ? seems is wrong name
Since upgrading to v1.1.2 (which contains the change from #6) I'm seeing the following error from some providers, including Google:
Access blocked: Authorization Error
Invalid parameter value for response_mode: 'web_message'
Error 400: invalid_request
I've tried overriding repsonse_mode
(to be query
) in both the OIDCClient
constructor and the loginWithPopup
parameters but it appears that the hard-coded value (web_message
) always takes precedence here:
Line 198 in 733d76c
Am I missing something or is this a regression? Any guidance would be appreciated. Thanks!
Hello,
As oidc-client-js got archived, I was looking for an alternative that can be used with any provider, and I jump into your project. So I played a bit with it testing, various configurations and I have some questions:
Currently the function OIDCClient.getIdToken
returns the raw idToken
do you think it could be meaningful to add a way to get a parsed idToken
? Or maybe adding parseJwt
to the list of exported function, in order to be able to decode it if needed ?
I tried the library with response_type="code"
, autoSilentRenew=true
, and useRefreshToken=true
. However during the silentLogin
it was silently not working. After logging SILENT_RENEW_ERROR
event error, I was able to get the error:
TypeError: Cannot read property 'nonce' of undefined
at OIDCClient._callee23$ (plusauth-oidc-client.es.js:9460)
at tryCatch (plusauth-oidc-client.es.js:4566)
at Generator.invoke [as _invoke] (plusauth-oidc-client.es.js:4796)
at Generator.next (plusauth-oidc-client.es.js:4621)
at fulfilled (plusauth-oidc-client.es.js:5428)
The issue comes from the fact that in the function silentLogin
calls the function handleTokenResult
with the second parameter being undefined
, because the variable finalState
is equal to an empty object.
And then in the handleTokenResult
, it tries to access nonce
of undefined
.
Best regards
Hello,
I have some questions about the validation of the issuer.
From the documentation the issuer identifier is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components.
So http://openid.net/specs/connect/1.0/issuer
could be a valid issuer, but it isn't.
Also, when doing some tests locally, I use some local issuer on http://localhost:45543
for instance, but it is not a valid uri, so I had to comment locally the check.
https://github.com/PlusAuth/plusauth-oidc-client-js/blob/af8a49d4b36e916b03ab7eec1efcc6c5064ea157/src/utils/url.ts#L3-L6
https://github.com/PlusAuth/plusauth-oidc-client-js/blob/af8a49d4b36e916b03ab7eec1efcc6c5064ea157/src/client.ts#L83-L85
Best regards
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.