fix: oauth state is encrypted

This commit is contained in:
diced
2024-11-25 22:36:55 -08:00
parent 523219273a
commit da875e451c
6 changed files with 41 additions and 18 deletions

View File

@@ -11,7 +11,7 @@ export function findProvider(
export const githubAuth = {
url: (clientId: string, state?: string, redirectUri?: string) =>
`https://github.com/login/oauth/authorize?client_id=${clientId}&scope=read:user${
state ? `&state=${state}` : ''
state ? `&state=${encodeURIComponent(state)}` : ''
}${redirectUri ? `&redirect_uri=${encodeURIComponent(redirectUri)}` : ''}`,
user: async (accessToken: string) => {
const res = await fetch('https://api.github.com/user', {
@@ -29,7 +29,7 @@ export const discordAuth = {
url: (clientId: string, origin: string, state?: string, redirectUri?: string) =>
`https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(
redirectUri ?? `${origin}/api/auth/oauth/discord`,
)}&response_type=code&scope=identify${state ? `&state=${state}` : ''}`,
)}&response_type=code&scope=identify${state ? `&state=${encodeURIComponent(state)}` : ''}`,
user: async (accessToken: string) => {
const res = await fetch('https://discord.com/api/users/@me', {
headers: {
@@ -47,7 +47,7 @@ export const googleAuth = {
`https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${encodeURIComponent(
redirectUri ?? `${origin}/api/auth/oauth/google`,
)}&response_type=code&access_type=offline&scope=https://www.googleapis.com/auth/userinfo.profile${
state ? `&state=${state}` : ''
state ? `&state=${encodeURIComponent(state)}` : ''
}`,
user: async (accessToken: string) => {
const res = await fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {
@@ -65,7 +65,7 @@ export const oidcAuth = {
url: (clientId: string, origin: string, authorizeUrl: string, state?: string, redirectUri?: string) =>
`${authorizeUrl}?client_id=${clientId}&redirect_uri=${encodeURIComponent(
redirectUri ?? `${origin}/api/auth/oauth/oidc`,
)}&response_type=code&scope=openid+email+profile+offline_access${state ? `&state=${state}` : ''}`,
)}&response_type=code&scope=openid+email+profile+offline_access${state ? `&state=${encodeURIComponent(state)}` : ''}`,
user: async (accessToken: string, userInfoUrl: string) => {
const res = await fetch(userInfoUrl, {
headers: {

View File

@@ -2,7 +2,7 @@ import { NextApiReq, NextApiRes } from '@/lib/response';
import { OAuthProviderType } from '@prisma/client';
import { prisma } from '../db';
import { findProvider } from './providerUtil';
import { createToken } from '../crypto';
import { createToken, decrypt } from '../crypto';
import { config } from '../config';
import { User } from '../db/models/user';
import Logger, { log } from '../logger';
@@ -88,7 +88,14 @@ export const withOAuth =
const userOauth = findProvider(provider, user?.oauthProviders ?? []);
if (state === 'link') {
let urlState;
try {
urlState = decrypt(decodeURIComponent(state ?? ''), config.core.secret);
} catch {
urlState = null;
}
if (urlState === 'link') {
if (!user) return res.unauthorized('invalid session');
if (findProvider(provider, user.oauthProviders))

View File

@@ -6,8 +6,9 @@ import enabled from '@/lib/oauth/enabled';
import { discordAuth } from '@/lib/oauth/providerUtil';
import { fetchToDataURL } from '@/lib/base64';
import Logger from '@/lib/logger';
import { encrypt } from '@/lib/crypto';
async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
async function handler({ code, host }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
if (!config.features.oauthRegistration)
return {
error: 'OAuth registration is disabled.',
@@ -22,15 +23,18 @@ async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promi
error_code: 401,
};
if (!code)
if (!code) {
const linkState = encrypt('link', config.core.secret);
return {
redirect: discordAuth.url(
config.oauth.discord.clientId!,
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
state,
linkState,
config.oauth.discord.redirectUri ?? undefined,
),
};
}
const body = new URLSearchParams({
client_id: config.oauth.discord.clientId!,

View File

@@ -1,5 +1,6 @@
import { fetchToDataURL } from '@/lib/base64';
import { config } from '@/lib/config';
import { encrypt } from '@/lib/crypto';
import Logger from '@/lib/logger';
import { combine } from '@/lib/middleware/combine';
import { method } from '@/lib/middleware/method';
@@ -7,7 +8,7 @@ import enabled from '@/lib/oauth/enabled';
import { githubAuth } from '@/lib/oauth/providerUtil';
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
async function handler({ code, state }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
async function handler({ code }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
if (!config.features.oauthRegistration)
return {
error: 'OAuth registration is disabled.',
@@ -22,14 +23,17 @@ async function handler({ code, state }: OAuthQuery, logger: Logger): Promise<OAu
error_code: 401,
};
if (!code)
if (!code) {
const linkState = encrypt('link', config.core.secret);
return {
redirect: githubAuth.url(
config.oauth.github.clientId!,
state,
linkState,
config.oauth.github.redirectUri ?? undefined,
),
};
}
const body = JSON.stringify({
client_id: config.oauth.github.clientId!,

View File

@@ -1,5 +1,6 @@
import { fetchToDataURL } from '@/lib/base64';
import { config } from '@/lib/config';
import { encrypt } from '@/lib/crypto';
import Logger from '@/lib/logger';
import { combine } from '@/lib/middleware/combine';
import { method } from '@/lib/middleware/method';
@@ -7,7 +8,7 @@ import enabled from '@/lib/oauth/enabled';
import { googleAuth } from '@/lib/oauth/providerUtil';
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
async function handler({ code, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
if (!config.features.oauthRegistration)
return {
error: 'OAuth registration is disabled.',
@@ -22,15 +23,18 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
error_code: 401,
};
if (!code)
if (!code) {
const linkState = encrypt('link', config.core.secret);
return {
redirect: googleAuth.url(
config.oauth.google.clientId!,
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
state,
linkState,
config.oauth.google.redirectUri ?? undefined,
),
};
}
const body = new URLSearchParams({
client_id: config.oauth.google.clientId!,

View File

@@ -1,4 +1,5 @@
import { config } from '@/lib/config';
import { encrypt } from '@/lib/crypto';
import Logger from '@/lib/logger';
import { combine } from '@/lib/middleware/combine';
import { method } from '@/lib/middleware/method';
@@ -7,7 +8,7 @@ import { oidcAuth } from '@/lib/oauth/providerUtil';
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
// thanks to @danejur for this https://github.com/diced/zipline/pull/372
async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
async function handler({ code, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
if (!config.features.oauthRegistration)
return {
error: 'OAuth registration is disabled.',
@@ -22,16 +23,19 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
error_code: 401,
};
if (!code)
if (!code) {
const linkState = encrypt('link', config.core.secret);
return {
redirect: oidcAuth.url(
config.oauth.oidc.clientId!,
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
config.oauth.oidc.authorizeUrl!,
state,
linkState,
config.oauth.oidc.redirectUri ?? undefined,
),
};
}
const body = new URLSearchParams({
client_id: config.oauth.oidc.clientId!,