Skip to content

Commit

Permalink
feat: use generic type for custom user claims
Browse files Browse the repository at this point in the history
OKTA-493385
<<<Jenkins Check-In of Tested SHA: fe0ce3c for [email protected]>>>
Artifact: okta-auth-js
Files changed count: 23
PR Link: #1213
  • Loading branch information
aarongranick-okta authored and eng-prod-CI-bot-okta committed May 20, 2022
1 parent f79b78c commit 7077d63
Show file tree
Hide file tree
Showing 23 changed files with 1,039 additions and 463 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ module.exports = {
},
},
{
files: ["rollup.config.js"],
files: [
"rollup.config.js",
"jest.esm.mjs"
],
rules: {
"node/no-unsupported-features/es-syntax": 0
}
Expand Down
18 changes: 17 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@
"env": {
"NODE_ENV": "development"
}
}
},

{
"type": "node",
"request": "launch",
"name": "Debug TSD",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": [
"--runInBand",
"--no-cache",
"--collectCoverage=false",
"--config=jest.tsd.js",
"--testTimeout=120000"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
]
}
3 changes: 2 additions & 1 deletion jest.esm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export default {
transform: {},
restoreMocks: true,
moduleNameMapper: {
'^@okta/okta-auth-js$': OktaAuth
'^@okta/okta-auth-js$': OktaAuth,
'^broadcast-channel$': '<rootDir>/node_modules/broadcast-channel/dist/es5node/index.js'
},
extensionsToTreatAsEsm: ['.ts'],
testPathIgnorePatterns: [],
Expand Down
22 changes: 22 additions & 0 deletions jest.tsd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
let counts = {};

module.exports = {
runner: 'jest-runner-tsd',
roots: [
'test/types'
],
testMatch: ['**/*.test-d.ts'],
reporters: [
'default',
['jest-junit', {
suiteNameTemplate: (vars) => `${vars.filename}`,
classNameTemplate: (vars) => {
if (!counts[vars.filename]) {
counts[vars.filename] = 0;
}
counts[vars.filename] = counts[vars.filename] + 1;
return `${vars.filename} ${counts[vars.filename]}`;
}
}]
]
};
10 changes: 8 additions & 2 deletions lib/OktaAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
RequestOptions,
IsAuthenticatedOptions,
OAuthResponseType,
CustomUserClaims,
} from './types';
import {
transactionStatus,
Expand Down Expand Up @@ -273,7 +274,12 @@ class OktaAuth implements OktaAuthInterface, SigninAPI, SignoutAPI {
renew: renewToken.bind(null, this),
renewTokensWithRefresh: renewTokensWithRefresh.bind(null, this),
renewTokens: renewTokens.bind(null, this),
getUserInfo: getUserInfo.bind(null, this),
getUserInfo: <C extends CustomUserClaims = CustomUserClaims>(
accessTokenObject: AccessToken,
idTokenObject: IDToken
): Promise<UserClaims<C>> => {
return getUserInfo(this, accessTokenObject, idTokenObject);
},
verify: verifyToken.bind(null, this),
isLoginRedirect: isLoginRedirect.bind(null, this)
};
Expand Down Expand Up @@ -614,7 +620,7 @@ class OktaAuth implements OktaAuthInterface, SigninAPI, SignoutAPI {
return !!(accessToken && idToken);
}

async getUser(): Promise<UserClaims> {
async getUser<T extends CustomUserClaims = CustomUserClaims>(): Promise<UserClaims<T>> {
const { idToken, accessToken } = this.tokenManager.getTokensSync();
return this.token.getUserInfo(accessToken, idToken);
}
Expand Down
7 changes: 5 additions & 2 deletions lib/oidc/getUserInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
import { isFunction } from '../util';
import { AuthSdkError, OAuthError } from '../errors';
import { httpRequest } from '../http';
import { AccessToken, IDToken, UserClaims, isAccessToken, isIDToken } from '../types';
import { AccessToken, IDToken, UserClaims, isAccessToken, isIDToken, CustomUserClaims } from '../types';

export async function getUserInfo(sdk, accessTokenObject: AccessToken, idTokenObject: IDToken): Promise<UserClaims> {
export async function getUserInfo<T extends CustomUserClaims = CustomUserClaims>(
sdk, accessTokenObject: AccessToken,
idTokenObject: IDToken
): Promise<UserClaims<T>> {
// If token objects were not passed, attempt to read from the TokenManager
if (!accessTokenObject) {
accessTokenObject = (await sdk.tokenManager.getTokens()).accessToken as AccessToken;
Expand Down
6 changes: 5 additions & 1 deletion lib/types/UserClaims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
*/

/* eslint-disable camelcase */
export type UserClaims<T = Record<string, string | boolean | number>> = T & {
export type CustomUserClaimValue = string | boolean | number;

export type CustomUserClaims = Record<string, CustomUserClaimValue | CustomUserClaimValue[]>;

export type UserClaims<T extends CustomUserClaims = CustomUserClaims> = T & {
auth_time?: number;
aud?: string;
email?: string;
Expand Down
7 changes: 5 additions & 2 deletions lib/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { AuthTransaction } from '../tx/AuthTransaction';
import { Token, Tokens, RevocableToken, AccessToken, IDToken, RefreshToken } from './Token';
import { JWTObject } from './JWT';
import { UserClaims } from './UserClaims';
import { CustomUserClaims, UserClaims } from './UserClaims';
import { CustomUrls, OktaAuthOptions, TokenParams } from './OktaAuthOptions';
import { StorageManager } from '../StorageManager';
import TransactionManager from '../TransactionManager';
Expand Down Expand Up @@ -157,7 +157,10 @@ export interface BaseTokenAPI {
}

export interface TokenAPI extends BaseTokenAPI {
getUserInfo(accessToken?: AccessToken, idToken?: IDToken): Promise<UserClaims>;
getUserInfo<S extends CustomUserClaims = CustomUserClaims>(
accessToken?: AccessToken,
idToken?: IDToken
): Promise<UserClaims<S>>;
getWithRedirect: GetWithRedirectAPI;
parseFromUrl: ParseFromUrlInterface;
getWithoutPrompt(params?: TokenParams): Promise<TokenResponse>;
Expand Down
30 changes: 10 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"dev:esm": "NODE_ENV=development yarn build:esm --watch",
"dev:web": "cross-env NODE_ENV=development webpack --config webpack.config.js --watch",
"lint": "eslint --ext .js,.ts,.jsx .",
"tsd": "tsd",
"lint:report": "eslint -f checkstyle -o ./build2/reports/lint/eslint-checkstyle-result.xml .",
"validate": "yarn tsc --noEmit && yarn lint && yarn workspace @okta/test.app validate && yarn tsd",
"test": "yarn test:unit && yarn test:samples && yarn test:e2e",
Expand All @@ -50,6 +49,7 @@
"test:bundle:esm:browser": "cross-env BUNDLE_ENV=browser NODE_OPTIONS=--experimental-vm-modules jest --config ./jest.esm.mjs",
"test:bundle:esm:node": "cross-env BUNDLE_ENV=node NODE_OPTIONS=--experimental-vm-modules jest --config ./jest.esm.mjs",
"test:bundle:cjs": "cross-env BUNDLE_ENV=node jest --config ./jest.cjs.js",
"test:types": "jest --config ./jest.tsd.js --runInBand",
"build": "node scripts/build.js",
"build:cdn": "cross-env NODE_ENV=production webpack --config webpack.cdn.config.js",
"build:web": "cross-env NODE_ENV=production webpack --config webpack.config.js",
Expand Down Expand Up @@ -116,7 +116,8 @@
"@rollup/plugin-alias": "^3.1.8",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^3.0.0",
"@types/jest": "^27.4.0",
"@tsd/typescript": "^4.6.4",
"@types/jest": "^27.5.1",
"@types/node": "^14.0.3",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
Expand All @@ -136,16 +137,19 @@
"globby": "^6.1.0",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine-ajax": "^4.0.0",
"jest": "^27.5.1",
"jest": "^28.1.0",
"jest-environment-jsdom": "^28.1.0",
"jest-junit": "^13.0.0",
"jest-runner": "^28.1.0",
"jest-runner-tsd": "^3.0.0",
"json-loader": "0.5.4",
"lodash": "4.17.20",
"rollup": "^2.59.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-license": "^2.6.0",
"rollup-plugin-typescript2": "^0.30.0",
"shelljs": "0.8.4",
"ts-jest": "^27.1.3",
"ts-jest": "^28.0.2",
"tsd": "^0.17.0",
"typescript": "^4.2.3",
"webpack": "^5.60.0",
Expand All @@ -171,20 +175,6 @@
]
},
"tsd": {
"directory": "test/types",
"compilerOptions": {
"skipLibCheck": true,
"types": [
"node"
],
"lib": [
"dom"
],
"paths": {
"@okta/okta-auth-js": [
"./build"
]
}
}
"directory": "test/types"
}
}
}
5 changes: 0 additions & 5 deletions scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ source ${OKTA_HOME}/${REPO}/scripts/setup.sh
export TEST_SUITE_TYPE="checkstyle"
export TEST_RESULT_FILE_DIR="${REPO}/build2"

if ! yarn tsd; then
echo "tsd failed! Exiting..."
exit ${TEST_FAILURE}
fi

if ! yarn lint:report; then
echo "lint failed! Exiting..."
exit ${TEST_FAILURE}
Expand Down
5 changes: 5 additions & 0 deletions scripts/unit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ if ! yarn test:unit; then
exit ${TEST_FAILURE}
fi

if ! yarn test:types; then
echo "tsd test failed! Exiting..."
exit ${TEST_FAILURE}
fi

if ! yarn test:bundle:esm:browser; then
echo "validate ESM browser bundle failed! Exiting..."
exit ${TEST_FAILURE}
Expand Down
3 changes: 2 additions & 1 deletion test/support/jest/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ module.exports = {
'clearMocks': true,
'moduleNameMapper': {
'^@okta/okta-auth-js$': OktaAuth,
'^lib/(.*)$': '<rootDir>/lib/$1'
'^lib/(.*)$': '<rootDir>/lib/$1',
'^jsonpath-plus$': '<rootDir>/node_modules/jsonpath-plus/dist/index-browser-umd.cjs'
},
'testPathIgnorePatterns': [],
'reporters': [
Expand Down
2 changes: 1 addition & 1 deletion test/types/auth.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and limitations under the License.
*
*/
import { AuthTransaction, UserClaims, OktaAuth, TokenParams } from '@okta/okta-auth-js';
import { AuthTransaction, UserClaims, OktaAuth, TokenParams } from '../../build/lib/index.d';
import { expectType, expectAssignable } from 'tsd';

const authClient = new OktaAuth({});
Expand Down
2 changes: 1 addition & 1 deletion test/types/authStateManager.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
RefreshToken,
AuthState,
OktaAuth
} from '@okta/okta-auth-js';
} from '../../build/lib/index.d';
import { expectType } from 'tsd';

const authClient = new OktaAuth({});
Expand Down
10 changes: 5 additions & 5 deletions test/types/config.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@ import {
OktaAuth,
OktaAuthOptions,
toRelativeUrl
} from '@okta/okta-auth-js';
} from '../../build/lib/index.d';
import { expectType } from 'tsd';

// Custom storage provider
const myMemoryStore = {};
const myMemoryStore: Record<string, string> = {};
const storageProvider = {
getItem: function(key) {
getItem: function(key: string): string {
// custom get
return myMemoryStore[key];
},
setItem: function(key, val) {
setItem: function(key: string, val: string) {
// custom set
myMemoryStore[key] = val;
},
// optional
removeItem: function(key) {
removeItem: function(key: string) {
delete myMemoryStore[key];
}
};
Expand Down
2 changes: 1 addition & 1 deletion test/types/parseFromUrl.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import {
OktaAuth, TokenResponse
} from '@okta/okta-auth-js';
} from '../../build/lib/index.d';
import { expectType } from 'tsd';

const authClient = new OktaAuth({});
Expand Down
2 changes: 1 addition & 1 deletion test/types/session.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and limitations under the License.
*
*/
import { SessionObject, OktaAuth } from '@okta/okta-auth-js';
import { SessionObject, OktaAuth } from '../../build/lib/index.d';
import { expectType } from 'tsd';

const authClient = new OktaAuth({});
Expand Down
25 changes: 23 additions & 2 deletions test/types/token.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {
JWTObject,
RefreshToken,
OktaAuth
} from '@okta/okta-auth-js';
import { expectType, expectAssignable } from 'tsd';
} from '../../build/lib/index.d';
import { expectType, expectAssignable, expectError } from 'tsd';

const authClient = new OktaAuth({});

Expand Down Expand Up @@ -111,3 +111,24 @@ const tokens = {

expectType<TokenParams>(await authClient.token.prepareTokenParams(authorizeOptions));
})();

// UserClaims
(async () => {

const basicUserClaims = await authClient.getUser();
expectType<UserClaims>(basicUserClaims);

type MyCustomClaims = {
groups: string[];
isAdmin: boolean;
age: number;
};

const customUserClaims = await authClient.getUser<MyCustomClaims>();
expectType<UserClaims<MyCustomClaims>>(customUserClaims);

expectError(() => {
type InvalidCustomClaims = UserClaims<{ func: () => boolean }>;
});

})();
10 changes: 6 additions & 4 deletions test/types/tokenManager.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import {
Tokens,
TokenManager,
RefreshToken,
OktaAuth
} from '@okta/okta-auth-js';
OktaAuth,
TokenManagerError
} from '../../build/lib/index.d';
import { expectType, expectAssignable } from 'tsd';

const authClient = new OktaAuth({});
Expand Down Expand Up @@ -58,8 +59,9 @@ const authClient = new OktaAuth({});
expectType<Token>(newToken);
expectType<Token>(oldToken!);
});
tokenManager.on('error', function (error) {
expectAssignable<object>(error);
tokenManager.on('error', function (error: TokenManagerError) {
expectType<TokenManagerError>(error);
expectAssignable<Error>(error);
});
tokenManager.off('error', () => {});
tokenManager.off('error');
Expand Down
2 changes: 1 addition & 1 deletion test/types/transaction.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and limitations under the License.
*
*/
import { AuthTransaction, OktaAuth, IdxStatus } from '@okta/okta-auth-js';
import { AuthTransaction, OktaAuth, IdxStatus } from '../../build/lib/index.d';
import { expectType } from 'tsd';

const authClient = new OktaAuth({});
Expand Down
Loading

0 comments on commit 7077d63

Please sign in to comment.