Add audience parameter to OAuth 2

Closes https://feedback.yaak.app/p/how-do-i-send-an-audience-using-oauth2
This commit is contained in:
Gregory Schier
2025-05-16 07:17:22 -07:00
parent 8c0f889dd2
commit 9615d3e29b
6 changed files with 26 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ export async function getAccessToken(
ctx: Context, { ctx: Context, {
accessTokenUrl, accessTokenUrl,
scope, scope,
audience,
params, params,
grantType, grantType,
credentialsInBody, credentialsInBody,
@@ -17,6 +18,7 @@ export async function getAccessToken(
grantType: string; grantType: string;
accessTokenUrl: string; accessTokenUrl: string;
scope: string | null; scope: string | null;
audience: string | null;
credentialsInBody: boolean; credentialsInBody: boolean;
params: HttpUrlParameter[]; params: HttpUrlParameter[];
}): Promise<AccessTokenRawResponse> { }): Promise<AccessTokenRawResponse> {
@@ -39,6 +41,7 @@ export async function getAccessToken(
}; };
if (scope) httpRequest.body!.form.push({ name: 'scope', value: scope }); if (scope) httpRequest.body!.form.push({ name: 'scope', value: scope });
if (scope) httpRequest.body!.form.push({ name: 'audience', value: audience });
if (credentialsInBody) { if (credentialsInBody) {
httpRequest.body!.form.push({ name: 'client_id', value: clientId }); httpRequest.body!.form.push({ name: 'client_id', value: clientId });

View File

@@ -19,6 +19,7 @@ export async function getAuthorizationCode(
redirectUri, redirectUri,
scope, scope,
state, state,
audience,
credentialsInBody, credentialsInBody,
pkce, pkce,
}: { }: {
@@ -29,6 +30,7 @@ export async function getAuthorizationCode(
redirectUri: string | null; redirectUri: string | null;
scope: string | null; scope: string | null;
state: string | null; state: string | null;
audience: string | null;
credentialsInBody: boolean; credentialsInBody: boolean;
pkce: { pkce: {
challengeMethod: string | null; challengeMethod: string | null;
@@ -53,6 +55,7 @@ export async function getAuthorizationCode(
if (redirectUri) authorizationUrl.searchParams.set('redirect_uri', redirectUri); if (redirectUri) authorizationUrl.searchParams.set('redirect_uri', redirectUri);
if (scope) authorizationUrl.searchParams.set('scope', scope); if (scope) authorizationUrl.searchParams.set('scope', scope);
if (state) authorizationUrl.searchParams.set('state', state); if (state) authorizationUrl.searchParams.set('state', state);
if (audience) authorizationUrl.searchParams.set('audience', audience);
if (pkce) { if (pkce) {
const verifier = pkce.codeVerifier || createPkceCodeVerifier(); const verifier = pkce.codeVerifier || createPkceCodeVerifier();
const challengeMethod = pkce.challengeMethod || DEFAULT_PKCE_METHOD; const challengeMethod = pkce.challengeMethod || DEFAULT_PKCE_METHOD;
@@ -95,6 +98,7 @@ export async function getAuthorizationCode(
clientId, clientId,
clientSecret, clientSecret,
scope, scope,
audience,
credentialsInBody, credentialsInBody,
params: [ params: [
{ name: 'code', value: code }, { name: 'code', value: code },

View File

@@ -10,12 +10,14 @@ export async function getClientCredentials(
clientId, clientId,
clientSecret, clientSecret,
scope, scope,
audience,
credentialsInBody, credentialsInBody,
}: { }: {
accessTokenUrl: string; accessTokenUrl: string;
clientId: string; clientId: string;
clientSecret: string; clientSecret: string;
scope: string | null; scope: string | null;
audience: string | null;
credentialsInBody: boolean; credentialsInBody: boolean;
}, },
) { ) {
@@ -29,6 +31,7 @@ export async function getClientCredentials(
const response = await getAccessToken(ctx, { const response = await getAccessToken(ctx, {
grantType: 'client_credentials', grantType: 'client_credentials',
accessTokenUrl, accessTokenUrl,
audience,
clientId, clientId,
clientSecret, clientSecret,
scope, scope,

View File

@@ -11,6 +11,7 @@ export function getImplicit(
redirectUri, redirectUri,
scope, scope,
state, state,
audience,
}: { }: {
authorizationUrl: string; authorizationUrl: string;
responseType: string; responseType: string;
@@ -18,6 +19,7 @@ export function getImplicit(
redirectUri: string | null; redirectUri: string | null;
scope: string | null; scope: string | null;
state: string | null; state: string | null;
audience: string | null;
}, },
) :Promise<AccessToken> { ) :Promise<AccessToken> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
@@ -34,6 +36,7 @@ export function getImplicit(
if (redirectUri) authorizationUrl.searchParams.set('redirect_uri', redirectUri); if (redirectUri) authorizationUrl.searchParams.set('redirect_uri', redirectUri);
if (scope) authorizationUrl.searchParams.set('scope', scope); if (scope) authorizationUrl.searchParams.set('scope', scope);
if (state) authorizationUrl.searchParams.set('state', state); if (state) authorizationUrl.searchParams.set('state', state);
if (audience) authorizationUrl.searchParams.set('audience', audience);
if (responseType.includes('id_token')) { if (responseType.includes('id_token')) {
authorizationUrl.searchParams.set('nonce', String(Math.floor(Math.random() * 9999999999999) + 1)); authorizationUrl.searchParams.set('nonce', String(Math.floor(Math.random() * 9999999999999) + 1));
} }

View File

@@ -13,6 +13,7 @@ export async function getPassword(
username, username,
password, password,
credentialsInBody, credentialsInBody,
audience,
scope, scope,
}: { }: {
accessTokenUrl: string; accessTokenUrl: string;
@@ -21,6 +22,7 @@ export async function getPassword(
username: string; username: string;
password: string; password: string;
scope: string | null; scope: string | null;
audience: string | null;
credentialsInBody: boolean; credentialsInBody: boolean;
}, },
): Promise<AccessToken> { ): Promise<AccessToken> {
@@ -40,6 +42,7 @@ export async function getPassword(
clientId, clientId,
clientSecret, clientSecret,
scope, scope,
audience,
grantType: 'password', grantType: 'password',
credentialsInBody, credentialsInBody,
params: [ params: [

View File

@@ -156,6 +156,12 @@ export const plugin: PluginDefinition = {
optional: true, optional: true,
dynamic: hiddenIfNot(['authorization_code', 'implicit']), dynamic: hiddenIfNot(['authorization_code', 'implicit']),
}, },
{
type: 'text',
name: 'audience',
label: 'Audience',
optional: true,
},
{ {
type: 'checkbox', type: 'checkbox',
name: 'usePkce', name: 'usePkce',
@@ -258,6 +264,7 @@ export const plugin: PluginDefinition = {
clientSecret: stringArg(values, 'clientSecret'), clientSecret: stringArg(values, 'clientSecret'),
redirectUri: stringArgOrNull(values, 'redirectUri'), redirectUri: stringArgOrNull(values, 'redirectUri'),
scope: stringArgOrNull(values, 'scope'), scope: stringArgOrNull(values, 'scope'),
audience: stringArgOrNull(values, 'audience'),
state: stringArgOrNull(values, 'state'), state: stringArgOrNull(values, 'state'),
credentialsInBody, credentialsInBody,
pkce: values.usePkce ? { pkce: values.usePkce ? {
@@ -273,6 +280,7 @@ export const plugin: PluginDefinition = {
redirectUri: stringArgOrNull(values, 'redirectUri'), redirectUri: stringArgOrNull(values, 'redirectUri'),
responseType: stringArg(values, 'responseType'), responseType: stringArg(values, 'responseType'),
scope: stringArgOrNull(values, 'scope'), scope: stringArgOrNull(values, 'scope'),
audience: stringArgOrNull(values, 'audience'),
state: stringArgOrNull(values, 'state'), state: stringArgOrNull(values, 'state'),
}); });
} else if (grantType === 'client_credentials') { } else if (grantType === 'client_credentials') {
@@ -282,6 +290,7 @@ export const plugin: PluginDefinition = {
clientId: stringArg(values, 'clientId'), clientId: stringArg(values, 'clientId'),
clientSecret: stringArg(values, 'clientSecret'), clientSecret: stringArg(values, 'clientSecret'),
scope: stringArgOrNull(values, 'scope'), scope: stringArgOrNull(values, 'scope'),
audience: stringArgOrNull(values, 'audience'),
credentialsInBody, credentialsInBody,
}); });
} else if (grantType === 'password') { } else if (grantType === 'password') {
@@ -293,6 +302,7 @@ export const plugin: PluginDefinition = {
username: stringArg(values, 'username'), username: stringArg(values, 'username'),
password: stringArg(values, 'password'), password: stringArg(values, 'password'),
scope: stringArgOrNull(values, 'scope'), scope: stringArgOrNull(values, 'scope'),
audience: stringArgOrNull(values, 'audience'),
credentialsInBody, credentialsInBody,
}); });
} else { } else {