Skip to content

Commit e89938e

Browse files
authored
Merge pull request #180 from thivi/master
Fix bugs with correlating authorization and access token requests
2 parents 47e2793 + dc374f6 commit e89938e

10 files changed

+2195
-3313
lines changed

README.md

+45-7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- [getPKCECode](#getPKCECode)
3535
- [setPKCECode](#setPKCECode)
3636
- [isSignOutSuccessful](#isSignOutSuccessful)
37+
- [didSignOutFail](#didSignOutFail)
3738
- [updateConfig](#updateConfig)
3839
- [Data Storage](#data-storage)
3940
- [Data Layer](#data-layer)
@@ -717,10 +718,13 @@ const isAuth = await auth.isAuthenticated();
717718
### getPKCECode
718719

719720
```TypeScript
720-
getPKCECode(userID?: string): string
721+
getPKCECode(state: string, userID?: string): string
721722
```
722723
#### Argument
723-
1. userID: `string` (optional)
724+
1. state: `string`
725+
The state parameter that was passed in the authorization request.
726+
727+
2. userID: `string` (optional)
724728

725729
If you want to use the SDK to manage multiple user sessions, you can pass a unique ID here. This can be useful when this SDK is used in backend applications.
726730
#### Returns
@@ -736,23 +740,25 @@ This code returns the PKCE code generated when the authorization URL is generate
736740
#### Example
737741

738742
```TypeScript
739-
const pkce = auth.getPKCECode();
743+
const pkce = auth.getPKCECode(state);
740744
```
741745

742746
---
743747

744748
### setPKCECode
745749

746750
```TypeScript
747-
setPKCECode(pkce: string, userID?: string): void
751+
setPKCECode(pkce: string, state: string, userID?: string): void
748752
```
749753

750754
#### Arguments
751755

752756
1. pkce: `string`
753757

754758
The PKCE code generated by the [`getAuthorizationURL`](#getAuthorizationURL) method.
755-
2. userID: `string` (optional)
759+
2. state: `string`
760+
The state parameter that was passed in the authorization request.
761+
3. userID: `string` (optional)
756762

757763
If you want to use the SDK to manage multiple user sessions, you can pass a unique ID here. This can be useful when this SDK is used in backend applications.
758764
#### Description
@@ -762,7 +768,7 @@ This method sets the PKCE code to the store. The PKCE code is usually stored in
762768
#### Example
763769

764770
```TypeScript
765-
auth.setPKCECode("pkce");
771+
auth.setPKCECode(pkce, state);
766772
```
767773

768774
---
@@ -789,7 +795,39 @@ A boolean value indicating if the user has been signed out or not.
789795

790796
#### Description
791797

792-
This method returns if the user has been successfully signed out or not. When a user signs out from the server, the user is redirected to the URL specified by the `signOutRedirectURL` in the config object passed into the constructor of the `AsgardeoAuthClient`. The server appends path parameters indicating if the sign-out is successful. This method reads the URL and returns if the sign-out is successful or not. So, make sure you pass as the argument, the URL to which the user has been redirected to after signing out from the server.
798+
This method returns if the user has been successfully signed out or not. When a user signs out from the server, the user is redirected to the URL specified by the `signOutRedirectURL` in the config object passed into the constructor of the `AsgardeoAuthClient`. The server appends path parameters indicating if the sign-out is successful. This method reads the URL and returns if the sign-out is successful or not. So, make sure you pass as the argument the URL to which the user has been redirected to after signing out from the server.
799+
800+
#### Example
801+
802+
```TypeScript
803+
const isSignedOut = auth.isSignOutSuccessful(window.location.href);
804+
```
805+
806+
---
807+
808+
### didSignOutFail
809+
810+
```TypeScript
811+
static didSignOutFail(signOutRedirectURL: string): boolean
812+
```
813+
814+
**This is a static method.**
815+
816+
#### Arguments
817+
818+
1. signOutRedirectURL: `string`
819+
820+
The URL to which the user is redirected to after signing out from the server.
821+
822+
#### Returns
823+
824+
didSignOutFail: `boolean`
825+
826+
A boolean value indicating if sign-out failed or not.
827+
828+
#### Description
829+
830+
This method returns if sign-out failed or not. When a user signs out from the server, the user is redirected to the URL specified by the `signOutRedirectURL` in the config object passed into the constructor of the `AsgardeoAuthClient`. The server appends path parameters indicating if the sign-out is successful. This method reads the URL and returns if the sign-out failed or not. So, make sure you pass as the argument the URL to which the user has been redirected to after signing out from the server.
793831

794832
#### Example
795833

lib/.eslintrc.js

+20-21
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ module.exports = {
55
jest: true,
66
node: true
77
},
8-
extends: ["eslint:recommended", "plugin:import/typescript"],
9-
overrides: [{
8+
extends: [ "eslint:recommended", "plugin:import/typescript" ],
9+
overrides: [ {
1010
env: {
1111
browser: true,
1212
es6: true,
@@ -17,7 +17,7 @@ module.exports = {
1717
"plugin:@typescript-eslint/eslint-recommended",
1818
"plugin:@typescript-eslint/recommended"
1919
],
20-
files: ["**/*.ts"],
20+
files: [ "**/*.ts" ],
2121
parser: "@typescript-eslint/parser",
2222
parserOptions: {
2323
ecmaVersion: 9,
@@ -27,50 +27,49 @@ module.exports = {
2727
"@typescript-eslint/explicit-function-return-type": 0,
2828
"@typescript-eslint/no-explicit-any": 0,
2929
"@typescript-eslint/no-inferrable-types": "off",
30-
"@typescript-eslint/no-unused-vars": "off",
31-
"@typescript-eslint/no-unused-vars-experimental": "warn",
32-
"@typescript-eslint/no-use-before-define": ["warn", {
30+
"@typescript-eslint/no-unused-vars": "warn",
31+
"@typescript-eslint/no-use-before-define": [ "warn", {
3332
classes: false,
3433
functions: false,
3534
typedefs: false,
3635
variables: false
37-
}],
36+
} ],
3837
"eol-last": "error",
3938
"no-debugger": "warn",
4039
"no-use-before-define": "off"
4140
}
42-
}],
41+
} ],
4342
parserOptions: {
4443
ecmaVersion: 9,
4544
sourceType: "module"
4645
},
47-
plugins: ["import"],
46+
plugins: [ "import", "@typescript-eslint" ],
4847
rules: {
49-
"comma-dangle": ["warn", "never"],
48+
"comma-dangle": [ "warn", "never" ],
5049
"eol-last": "error",
51-
"import/order": ["warn", {
50+
"import/order": [ "warn", {
5251
"alphabetize": {
5352
caseInsensitive: true,
5453
order: "asc"
5554
},
56-
"groups": ["builtin", "external", "index", "sibling", "parent", "internal"]
57-
}],
58-
"max-len": ["warn", {
55+
"groups": [ "builtin", "external", "index", "sibling", "parent", "internal" ]
56+
} ],
57+
"max-len": [ "warn", {
5958
"code": 120
60-
}],
59+
} ],
6160
"no-console": "warn",
6261
"no-duplicate-imports": "warn",
63-
"object-curly-spacing": ["warn", "always"],
64-
"quotes": ["warn", "double"],
65-
"sort-imports": ["warn", {
62+
"object-curly-spacing": [ "warn", "always" ],
63+
"quotes": [ "warn", "double" ],
64+
"sort-imports": [ "warn", {
6665
"ignoreCase": false,
6766
"ignoreDeclarationSort": true,
6867
"ignoreMemberSort": false
69-
}],
70-
"sort-keys": ["warn", "asc", {
68+
} ],
69+
"sort-keys": [ "warn", "asc", {
7170
"caseSensitive": true,
7271
"minKeys": 2,
7372
"natural": false
74-
}]
73+
} ]
7574
}
7675
};

lib/package.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,20 @@
2727
"author": "Asgardeo",
2828
"license": "Apache-2.0",
2929
"devDependencies": {
30-
"@rollup/plugin-commonjs": "^16.0.0",
31-
"@rollup/plugin-eslint": "^8.0.0",
30+
"@rollup/plugin-commonjs": "^21.0.2",
31+
"@rollup/plugin-eslint": "^8.0.1",
3232
"@rollup/plugin-json": "^4.1.0",
33-
"@rollup/plugin-node-resolve": "^10.0.0",
34-
"@rollup/plugin-replace": "^2.3.4",
35-
"@types/node": "^13.9.2",
36-
"rollup": "^2.33.3",
37-
"rollup-plugin-analyzer": "^3.3.0",
33+
"@rollup/plugin-node-resolve": "^13.1.3",
34+
"@rollup/plugin-replace": "^4.0.0",
35+
"@types/node": "^17.0.21",
36+
"rollup": "^2.69.0",
37+
"rollup-plugin-analyzer": "^4.0.0",
3838
"rollup-plugin-auto-external": "^2.0.0",
3939
"rollup-plugin-terser": "^7.0.2",
40-
"rollup-plugin-typescript2": "^0.29.0",
41-
"rollup-plugin-web-worker-loader": "^1.4.0",
40+
"rollup-plugin-typescript2": "^0.31.2",
41+
"rollup-plugin-web-worker-loader": "^1.6.1",
4242
"rollup-pluginutils": "^2.8.2",
43-
"typescript": "*"
43+
"typescript": "~4.5.5"
4444
},
4545
"repository": {
4646
"type": "git",

lib/src/client.ts

+33-8
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* under the License.
1717
*/
1818

19-
import { OIDC_SCOPE, OP_CONFIG_INITIATED, ResponseMode, SIGN_OUT_SUCCESS_PARAM } from "./constants";
19+
import { OIDC_SCOPE, OP_CONFIG_INITIATED, ResponseMode, SIGN_OUT_SUCCESS_PARAM, STATE } from "./constants";
2020
import { AuthenticationCore } from "./core";
2121
import { DataLayer } from "./data";
2222
import {
@@ -205,7 +205,7 @@ export class AsgardeoAuthClient<T> {
205205
}
206206

207207
return this._authenticationCore.getOIDCProviderMetaData(false).then(() => {
208-
return this._authenticationCore.requestAccessToken(authorizationCode, sessionState, state,userID);
208+
return this._authenticationCore.requestAccessToken(authorizationCode, sessionState, state, userID);
209209
});
210210
}
211211

@@ -498,6 +498,7 @@ export class AsgardeoAuthClient<T> {
498498
*
499499
* @param {string} userID - (Optional) A unique ID of the user to be authenticated. This is useful in multi-user
500500
* scenarios where each user should be uniquely identified.
501+
* @param {string} state - The state parameter that was passed in the authentication URL.
501502
*
502503
* @return {Promise<string>} - A Promise that resolves with the PKCE code.
503504
*
@@ -512,15 +513,15 @@ export class AsgardeoAuthClient<T> {
512513
*
513514
* @preserve
514515
*/
515-
public async getPKCECode(userID?: string): Promise<string> {
516-
return this._authenticationCore.getPKCECode(userID);
516+
public async getPKCECode(state: string, userID?: string): Promise<string> {
517+
return this._authenticationCore.getPKCECode(state, userID);
517518
}
518519

519520
/**
520521
* This method sets the PKCE code to the data store.
521522
*
522523
* @param {string} pkce - The PKCE code.
523-
*
524+
* @param {string} state - The state parameter that was passed in the authentication URL.
524525
* @param {string} userID - (Optional) A unique ID of the user to be authenticated. This is useful in multi-user
525526
* scenarios where each user should be uniquely identified.
526527
*
@@ -535,8 +536,8 @@ export class AsgardeoAuthClient<T> {
535536
*
536537
* @preserve
537538
*/
538-
public async setPKCECode(pkce: string, userID?: string): Promise<void> {
539-
await this._authenticationCore.setPKCECode(pkce, userID);
539+
public async setPKCECode(pkce: string, state: string, userID?: string): Promise<void> {
540+
await this._authenticationCore.setPKCECode(pkce, state, userID);
540541
}
541542

542543
/**
@@ -557,12 +558,36 @@ export class AsgardeoAuthClient<T> {
557558
*/
558559
public static isSignOutSuccessful(signOutRedirectURL: string): boolean {
559560
const url = new URL(signOutRedirectURL);
560-
const stateParam = url.searchParams.get("state");
561+
const stateParam = url.searchParams.get(STATE);
561562
const error = Boolean(url.searchParams.get("error"));
562563

563564
return stateParam ? stateParam === SIGN_OUT_SUCCESS_PARAM && !error : false;
564565
}
565566

567+
/**
568+
* This method returns if the sign-out has failed or not.
569+
*
570+
* @param {string} signOutRedirectUrl - The URL to which the user has been redirected to after signing-out.
571+
*
572+
* **The server appends path parameters to the `signOutRedirectURL` and these path parameters
573+
* are required for this method to function.**
574+
*
575+
* @return {boolean} - `true` if successful, `false` otherwise.
576+
*
577+
* @link https://github.com/asgardeo/asgardeo-auth-js-sdk/tree/master#didSignOutFail
578+
*
579+
* @memberof AsgardeoAuthClient
580+
*
581+
* @preserve
582+
*/
583+
public static didSignOutFail(signOutRedirectURL: string): boolean {
584+
const url = new URL(signOutRedirectURL);
585+
const stateParam = url.searchParams.get(STATE);
586+
const error = Boolean(url.searchParams.get("error"));
587+
588+
return stateParam ? stateParam === SIGN_OUT_SUCCESS_PARAM && error : false;
589+
}
590+
566591
/**
567592
* This method updates the configuration that was passed into the constructor when instantiating this class.
568593
*

lib/src/core/authentication-core.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
FetchCredentialTypes,
2121
OIDC_SCOPE,
2222
OP_CONFIG_INITIATED,
23-
PKCE_CODE_VERIFIER,
2423
SESSION_STATE,
2524
SIGN_OUT_SUCCESS_PARAM,
2625
STATE
@@ -119,17 +118,18 @@ export class AuthenticationCore<T> {
119118
const customParams = config;
120119
if (customParams) {
121120
for (const [ key, value ] of Object.entries(customParams)) {
122-
if (key != "" && value != "") {
121+
if (key != "" && value != "" && key !== STATE) {
123122
authorizeRequest.searchParams.append(key, value.toString());
124123
}
125124
}
126125
}
127126

127+
128128
authorizeRequest.searchParams.append(
129129
STATE,
130130
AuthenticationUtils.generateStateParamForRequestCorrelation(
131131
pkceKey,
132-
authorizeRequest.searchParams.get(STATE) ?? ""
132+
customParams ? customParams[STATE]?.toString() : ""
133133
)
134134
);
135135

@@ -644,12 +644,18 @@ export class AuthenticationCore<T> {
644644
return Boolean(await this.getAccessToken(userID));
645645
}
646646

647-
public async getPKCECode(userID?: string): Promise<string> {
648-
return (await this._dataLayer.getTemporaryDataParameter(PKCE_CODE_VERIFIER, userID)) as string;
647+
public async getPKCECode(state: string, userID?: string): Promise<string> {
648+
return (await this._dataLayer.getTemporaryDataParameter(
649+
AuthenticationUtils.extractPKCEKeyFromStateParam(state),
650+
userID)) as string;
649651
}
650652

651-
public async setPKCECode(pkce: string, userID?: string): Promise<void> {
652-
return await this._dataLayer.setTemporaryDataParameter(PKCE_CODE_VERIFIER, pkce, userID);
653+
public async setPKCECode(pkce: string, state: string, userID?: string): Promise<void> {
654+
return await this._dataLayer.setTemporaryDataParameter(
655+
AuthenticationUtils.extractPKCEKeyFromStateParam(state),
656+
pkce,
657+
userID
658+
);
653659
}
654660

655661
public async updateConfig(config: Partial<AuthClientConfig<T>>): Promise<void> {

lib/src/helpers/authentication-helper.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ export class AuthenticationHelper<T> {
9696
configData.endpoints &&
9797
Object.keys(configData.endpoints).forEach((endpointName: string) => {
9898
const snakeCasedName = endpointName.replace(/[A-Z]/g, (letter) => `_${ letter.toLowerCase() }`);
99-
oidcProviderMetaData[ snakeCasedName ] = configData?.endpoints ? configData.endpoints[ endpointName ] : "";
99+
oidcProviderMetaData[ snakeCasedName ] = configData?.endpoints
100+
? configData.endpoints[ endpointName ]
101+
: "";
100102
});
101103

102104
const defaultEndpoints = {

lib/src/helpers/crypto-helper.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,7 @@ export class CryptoHelper<T = any, R = any> {
5959

6060
for (const key of keys) {
6161
if (headerJSON.kid === key.kid) {
62-
return this._cryptoUtils.parseJwk({
63-
alg: key.alg,
64-
e: key.e,
65-
kty: key.kty,
66-
n: key.n
67-
});
62+
return this._cryptoUtils.parseJwk(key);
6863
}
6964
}
7065

0 commit comments

Comments
 (0)