React Native with Expo: /oauth/token endpoint always results in access_denied in PKCE flow

Yes! I got it! Your advice with Postman helped me see the error clearly. It turns out my code_verifier was incorrectly formed - it was too long.

Ultimately, the real issue was in what types are returned in node’s crypto package (as a Buffer) and expo’s crypto package (expo-crypto) (as a Uint8Array). I had to import the Buffer package and encode it to Base64 directly (using node’s encode package for Base64 encoding didn’t work for me)

The correct way to produce the code_verifier and code_challenge as specified by auth0 using expo packages is as follows:

import * as Crypto from 'expo-crypto';
import * as Random from 'expo-random';
import { Buffer } from 'buffer';

function URLEncode(str) {
    return str
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
}

async function sha256(buffer) {
    return await Crypto.digestStringAsync(
        Crypto.CryptoDigestAlgorithm.SHA256,
        buffer,
        { encoding: Crypto.CryptoEncoding.BASE64 }
    );
}

const randomBytes = await Random.getRandomBytesAsync(32);
const base64String = Buffer.from(randomBytes).toString('base64');
const code_verifier = URLEncode(base64String);
const code_challenge = URLEncode(await sha256(code_verifier));

(DON’T try and include the node crypto package as it’s deprecated) I hope this will prove useful to anyone else who finds this post!

If anyone is interested, I wrote a more detailed write up on these issues on my blog:

2 Likes