UNPKG

expo-auth-session

Version:

Expo module for browser-based authentication

62 lines (51 loc) 2.01 kB
import * as Crypto from 'expo-crypto'; import invariant from 'invariant'; const CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; function convertBufferToString(buffer: Uint8Array): string { const state: string[] = []; for (const byte of buffer) { const index = byte % CHARSET.length; const char = CHARSET[index]; if (char != null) { state.push(char); } } return state.join(''); } function convertToUrlSafeString(b64: string): string { return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); } export function generateRandom(size: number): string { const buffer = Crypto.getRandomValues(new Uint8Array(size)); return convertBufferToString(buffer); } /** * Proof key for Code Exchange by OAuth Public Clients (RFC 7636), Section 4.1 * [Section 4.1](https://tools.ietf.org/html/rfc7636#section-4.1) */ export async function deriveChallengeAsync(code: string): Promise<string> { // 43 is the minimum, and 128 is the maximum. invariant(code.length > 42 && code.length < 129, 'Invalid code length for PKCE.'); const buffer = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, code, { encoding: Crypto.CryptoEncoding.BASE64, }); return convertToUrlSafeString(buffer); } export async function buildCodeAsync( size: number = 128 ): Promise<{ codeChallenge: string; codeVerifier: string }> { // This method needs to be resolved like all other native methods. const codeVerifier = generateRandom(size); const codeChallenge = await deriveChallengeAsync(codeVerifier); return { codeVerifier, codeChallenge }; } /** * Digest a random string with hex encoding, useful for creating `nonce`s. */ export async function generateHexStringAsync(size: number): Promise<string> { const value = generateRandom(size); const buffer = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, value, { encoding: Crypto.CryptoEncoding.HEX, }); return convertToUrlSafeString(buffer); }