Skip to content

Commit

Permalink
Merge pull request #16 from cobuildlab/feature/token-validation
Browse files Browse the repository at this point in the history
improve token validation
  • Loading branch information
kikeztw authored Apr 5, 2022
2 parents 22bd572 + 444f022 commit 436daca
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 14 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const CREDENTIALS_HANDLER = {
await AsyncStorage.removeItem('credentials_store');
apolloClient.resetStore();
},
// You can specify a what token the library will validate
tokenToValidate: 'accessToken',
// or pass a function that validate it
validateToken: (credentials) => {
const { exp } = jwtDecode(credentials.idToken) as { exp: number };

Expand Down
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cobuildlab/react-native-auth0",
"version": "0.3.0",
"version": "0.3.1",
"description": "Todo",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
Expand Down Expand Up @@ -62,5 +62,8 @@
"prettier --write",
"eslint --fix"
]
},
"dependencies": {
"jwt-decode": "^3.1.2"
}
}
45 changes: 37 additions & 8 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import Auth0, {
AuthorizeOptions,
Credentials as Auth0Credentials,
Options,
} from 'react-native-auth0';
import { Credentials, CredentialsHandlersInput, ErrorCases } from './types';
import { ErrorPublisher } from './utils';
import {
Credentials,
CredentialsHandlersInput,
ErrorCases,
Options,
tokenToValidateType,
} from './types';
import { ErrorPublisher, validateToken } from './utils';

export class Auth0Native extends Auth0 {
private credentials: Credentials | null = null;
private audience: string;

private saveCredentials: CredentialsHandlersInput['save'];
private getCredentials: CredentialsHandlersInput['get'];
private clearCredentials: CredentialsHandlersInput['clear'];
private validateToken: CredentialsHandlersInput['validateToken'];

private validateToken?: CredentialsHandlersInput['validateToken'];
private tokenToValidate?: tokenToValidateType;
private errors: Record<ErrorCases, ErrorPublisher> = {
AUTHORIZATION: new ErrorPublisher(),
CLEAR_SESSION: new ErrorPublisher(),
REFRESH_TOKEN: new ErrorPublisher(),
SAVE_CREDENTIALS: new ErrorPublisher(),
};

/**
* Create a client to used to authenticate with auth0 platform and manage that auth state.
*
* @param {Options} options - Options.
* @param {CredentialsHandlersInput} credentialsHandlers - Options to handle the credentials and token validations.
*/
constructor(options: Options, credentialsHandlers: CredentialsHandlersInput) {
super(options);
this.saveCredentials = credentialsHandlers.save;
this.getCredentials = credentialsHandlers.get;
this.clearCredentials = credentialsHandlers.clear;
this.validateToken = credentialsHandlers.validateToken;
this.tokenToValidate = credentialsHandlers.tokenToValidate;

// set a default audience if the options is undefined
this.audience = options.audience || `https://${options.domain}/api/v2/`;
}

async handleCredentials(data: Auth0Credentials): Promise<Credentials> {
Expand All @@ -49,8 +65,8 @@ export class Auth0Native extends Auth0 {
/**
*
* @param {string} scope - Scopes requested for the issued tokens. E.g. `openid profile`.
* @param {object} options - Options to pass to the auth endpoint.
* @returns {object} The auth0 credentials.
* @param {AuthorizeOptions} options - Options to pass to the auth endpoint.
* @returns {Promise<Credentials | undefined>} The auth0 credentials.
*/
async authorize(
scope: string,
Expand All @@ -60,6 +76,7 @@ export class Auth0Native extends Auth0 {
const result = await this.webAuth.authorize(
{
scope,
audience: this.audience,
},
options,
);
Expand Down Expand Up @@ -90,8 +107,20 @@ export class Auth0Native extends Auth0 {
if (!credentials) {
return false;
}
let valid = false;

if (this.validateToken && this.validateToken(credentials)) {
valid = true;
}

if (
this.tokenToValidate &&
validateToken(credentials[this.tokenToValidate])
) {
valid = true;
}

if (this.validateToken(credentials)) {
if (valid) {
this.credentials = credentials;
return true;
}
Expand Down
24 changes: 21 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import {
AuthorizeOptions,
Credentials as Auth0Credentials,
Options as Auth0Options,
} from 'react-native-auth0';

export interface Credentials extends Auth0Credentials {
issuedAt?: number;
}

export type CredentialsHandlersInput = {
export interface Options extends Auth0Options {
audience?: string;
}
export type tokenToValidateType = keyof Pick<
Credentials,
'idToken' | 'accessToken'
>;
interface CredentialsHandlersInputBase {
save: (data: Credentials) => void | Promise<void>;
clear: () => void | Promise<void>;
get: () => (Credentials | null) | Promise<Credentials | null>;
validateToken: (data: Credentials) => boolean;
};
}
export type CredentialsHandlersInput = CredentialsHandlersInputBase &
(
| {
validateToken: (data: Credentials) => boolean;
tokenToValidate?: tokenToValidateType;
}
| {
tokenToValidate: tokenToValidateType;
validateToken?: (data: Credentials) => boolean;
}
);

export interface AuthClientContextType {
authorize: (args: {
Expand Down
12 changes: 12 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import jwtDecode from 'jwt-decode';
import { ErrorCallbackType } from './types';

/**.
Expand All @@ -9,6 +10,17 @@ export function getTimestamp(): number {
return Math.round(new Date().getTime() / 1000);
}

/**
* @param {string} token - Token to validate.
* @returns {boolean} Boolean if the token is valid.
*/
export function validateToken(token: string): boolean {
const tokenObj = jwtDecode<{
exp: number;
}>(token);

return tokenObj.exp > getTimestamp();
}
export class ErrorPublisher {
private subscribers: Set<ErrorCallbackType> = new Set();

Expand Down

0 comments on commit 436daca

Please sign in to comment.