Skip to content

Commit

Permalink
Merge pull request #6 from kinde-oss/feat/enhancements
Browse files Browse the repository at this point in the history
Enhancements
  • Loading branch information
DanielRivers authored Sep 11, 2024
2 parents a177a01 + 3eb0cec commit 0181a78
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 9 deletions.
15 changes: 14 additions & 1 deletion lib/sessionManager/stores/expoSecureStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import { splitString } from "../utils.js";

let expoSecureStore: typeof import("expo-secure-store") | undefined = undefined;

async function waitForExpoSecureStore() {
let tries = 0;
while (!expoSecureStore && tries < 20) {
await new Promise((resolve) => setTimeout(resolve, 100));
tries++;
}
}

/**
* Provides a expo local store based session manager implementation for the browser.
* @class ExpoSecureStore
Expand Down Expand Up @@ -42,6 +50,7 @@ export class ExpoSecureStore<V = StorageKeys> implements SessionManager<V> {
itemKey: V | StorageKeys,
itemValue: unknown,
): Promise<void> {
await waitForExpoSecureStore();
// clear items first
await this.removeSessionItem(itemKey);

Expand All @@ -66,6 +75,8 @@ export class ExpoSecureStore<V = StorageKeys> implements SessionManager<V> {
* @returns {unknown | null}
*/
async getSessionItem(itemKey: V | StorageKeys): Promise<unknown | null> {
await waitForExpoSecureStore();

const chunks = [];
let index = 0;

Expand All @@ -82,7 +93,7 @@ export class ExpoSecureStore<V = StorageKeys> implements SessionManager<V> {
);
}

return chunks.join("");
return chunks.join("") || null;
}

/**
Expand All @@ -91,6 +102,8 @@ export class ExpoSecureStore<V = StorageKeys> implements SessionManager<V> {
* @returns {void}
*/
async removeSessionItem(itemKey: V | StorageKeys): Promise<void> {
await waitForExpoSecureStore();

let index = 0;

let chunk = await expoSecureStore!.getItemAsync(
Expand Down
4 changes: 4 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export type LoginOptions = {
* Whether to show the success screen at the end of the flow, this is most useful when the callback is not a webpage.
*/
hasSuccessPage?: boolean;
/**
* Single use code to prevent replay attacks
*/
nonce?: string;
};

export enum IssuerRouteTypes {
Expand Down
18 changes: 13 additions & 5 deletions lib/utils/generateAuthUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ export const generateAuthUrl = (
...mapLoginMethodParamsForUrl(options),
};

const generatedState = generateRandomString(32);
const generatedNonce = generateRandomString(16);
if (!options.state) {
options.state = generateRandomString(32);
}
searchParams["state"] = options.state;

searchParams["state"] = options.state || generatedState;
searchParams["nonce"] = generatedNonce;
if (!options.nonce) {
options.nonce = generateRandomString(16);
}
searchParams["nonce"] = options.nonce;

if (options.codeChallenge) {
searchParams["code_challenge"] = options.codeChallenge;
Expand All @@ -38,5 +42,9 @@ export const generateAuthUrl = (
}

authUrl.search = new URLSearchParams(searchParams).toString();
return { url: authUrl, state: generatedState, nonce: generatedNonce };
return {
url: authUrl,
state: searchParams["state"],
nonce: searchParams["nonce"],
};
};
23 changes: 20 additions & 3 deletions lib/utils/generateRandomString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@
* @returns {string} required secret
*/
export const generateRandomString = (length: number = 28): string => {
const arr = new Uint8Array(length / 2);
crypto.getRandomValues(arr);
return Array.from(arr, dec2hex).join("");
if (crypto) {
const arr = new Uint8Array(length / 2);
crypto.getRandomValues(arr);
return Array.from(arr, dec2hex).join("");
} else {
return generateRandomStringNonCrypto(length);
}
};

function dec2hex(dec: number) {
return dec.toString(16).padStart(2, "0");
}

function generateRandomStringNonCrypto(length: number = 28) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;

for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}

return result;
}

0 comments on commit 0181a78

Please sign in to comment.