mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-26 11:21:30 +01:00
Fix PKCE flow and clean up other flows
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { Context } from '@yaakapp/api';
|
||||
import type { Context } from '@yaakapp/api';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { getAccessToken } from '../getAccessToken';
|
||||
import { fetchAccessToken } from '../fetchAccessToken';
|
||||
import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken';
|
||||
import { AccessToken, getDataDirKey, storeToken } from '../store';
|
||||
import type { AccessToken } from '../store';
|
||||
import { getDataDirKey, storeToken } from '../store';
|
||||
|
||||
export const PKCE_SHA256 = 'S256';
|
||||
export const PKCE_PLAIN = 'plain';
|
||||
@@ -34,8 +35,8 @@ export async function getAuthorizationCode(
|
||||
audience: string | null;
|
||||
credentialsInBody: boolean;
|
||||
pkce: {
|
||||
challengeMethod: string | null;
|
||||
codeVerifier: string | null;
|
||||
challengeMethod: string;
|
||||
codeVerifier: string;
|
||||
} | null;
|
||||
tokenName: 'access_token' | 'id_token';
|
||||
},
|
||||
@@ -59,26 +60,25 @@ export async function getAuthorizationCode(
|
||||
if (state) authorizationUrl.searchParams.set('state', state);
|
||||
if (audience) authorizationUrl.searchParams.set('audience', audience);
|
||||
if (pkce) {
|
||||
const verifier = pkce.codeVerifier || createPkceCodeVerifier();
|
||||
const challengeMethod = pkce.challengeMethod || DEFAULT_PKCE_METHOD;
|
||||
authorizationUrl.searchParams.set(
|
||||
'code_challenge',
|
||||
createPkceCodeChallenge(verifier, challengeMethod),
|
||||
pkceCodeChallenge(pkce.codeVerifier, pkce.challengeMethod),
|
||||
);
|
||||
authorizationUrl.searchParams.set('code_challenge_method', challengeMethod);
|
||||
authorizationUrl.searchParams.set('code_challenge_method', pkce.challengeMethod);
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
const logsEnabled = (await ctx.store.get('enable_logs')) ?? false;
|
||||
console.log('[oauth2] Authorizing', authorizationUrlStr);
|
||||
const logsEnabled = (await ctx.store.get('enable_logs')) ?? false;
|
||||
const dataDirKey = await getDataDirKey(ctx, contextId);
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
console.log('[oauth2] Authorizing', authorizationUrlStr);
|
||||
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
const code = await new Promise<string>(async (resolve, reject) => {
|
||||
let foundCode = false;
|
||||
|
||||
let { close } = await ctx.window.openUrl({
|
||||
const { close } = await ctx.window.openUrl({
|
||||
url: authorizationUrlStr,
|
||||
label: 'oauth-authorization-url',
|
||||
dataDirKey: await getDataDirKey(ctx, contextId),
|
||||
dataDirKey,
|
||||
async onClose() {
|
||||
if (!foundCode) {
|
||||
reject(new Error('Authorization window closed'));
|
||||
@@ -89,6 +89,7 @@ export async function getAuthorizationCode(
|
||||
if (logsEnabled) console.log('[oauth2] Navigated to', urlStr);
|
||||
|
||||
if (url.searchParams.has('error')) {
|
||||
close();
|
||||
return reject(new Error(`Failed to authorize: ${url.searchParams.get('error')}`));
|
||||
}
|
||||
|
||||
@@ -101,37 +102,35 @@ export async function getAuthorizationCode(
|
||||
// Close the window here, because we don't need it anymore!
|
||||
foundCode = true;
|
||||
close();
|
||||
|
||||
console.log('[oauth2] Code found');
|
||||
const response = await getAccessToken(ctx, {
|
||||
grantType: 'authorization_code',
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
audience,
|
||||
credentialsInBody,
|
||||
params: [
|
||||
{ name: 'code', value: code },
|
||||
...(redirectUri ? [{ name: 'redirect_uri', value: redirectUri }] : []),
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
resolve(await storeToken(ctx, contextId, response, tokenName));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(code);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[oauth2] Code found');
|
||||
const response = await fetchAccessToken(ctx, {
|
||||
grantType: 'authorization_code',
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
audience,
|
||||
credentialsInBody,
|
||||
params: [
|
||||
{ name: 'code', value: code },
|
||||
...(pkce ? [{ name: 'code_verifier', value: pkce.codeVerifier }] : []),
|
||||
...(redirectUri ? [{ name: 'redirect_uri', value: redirectUri }] : []),
|
||||
],
|
||||
});
|
||||
|
||||
return storeToken(ctx, contextId, response, tokenName);
|
||||
}
|
||||
|
||||
function createPkceCodeVerifier() {
|
||||
export function genPkceCodeVerifier() {
|
||||
return encodeForPkce(randomBytes(32));
|
||||
}
|
||||
|
||||
function createPkceCodeChallenge(verifier: string, method: string) {
|
||||
function pkceCodeChallenge(verifier: string, method: string) {
|
||||
if (method === 'plain') {
|
||||
return verifier;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user