Skip to content

Commit da875e4

Browse files
committed
fix: oauth state is encrypted
1 parent 5232192 commit da875e4

File tree

6 files changed

+41
-18
lines changed

6 files changed

+41
-18
lines changed

src/lib/oauth/providerUtil.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function findProvider(
1111
export const githubAuth = {
1212
url: (clientId: string, state?: string, redirectUri?: string) =>
1313
`https://github.com/login/oauth/authorize?client_id=${clientId}&scope=read:user${
14-
state ? `&state=${state}` : ''
14+
state ? `&state=${encodeURIComponent(state)}` : ''
1515
}${redirectUri ? `&redirect_uri=${encodeURIComponent(redirectUri)}` : ''}`,
1616
user: async (accessToken: string) => {
1717
const res = await fetch('https://api.github.com/user', {
@@ -29,7 +29,7 @@ export const discordAuth = {
2929
url: (clientId: string, origin: string, state?: string, redirectUri?: string) =>
3030
`https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(
3131
redirectUri ?? `${origin}/api/auth/oauth/discord`,
32-
)}&response_type=code&scope=identify${state ? `&state=${state}` : ''}`,
32+
)}&response_type=code&scope=identify${state ? `&state=${encodeURIComponent(state)}` : ''}`,
3333
user: async (accessToken: string) => {
3434
const res = await fetch('https://discord.com/api/users/@me', {
3535
headers: {
@@ -47,7 +47,7 @@ export const googleAuth = {
4747
`https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${encodeURIComponent(
4848
redirectUri ?? `${origin}/api/auth/oauth/google`,
4949
)}&response_type=code&access_type=offline&scope=https://www.googleapis.com/auth/userinfo.profile${
50-
state ? `&state=${state}` : ''
50+
state ? `&state=${encodeURIComponent(state)}` : ''
5151
}`,
5252
user: async (accessToken: string) => {
5353
const res = await fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {
@@ -65,7 +65,7 @@ export const oidcAuth = {
6565
url: (clientId: string, origin: string, authorizeUrl: string, state?: string, redirectUri?: string) =>
6666
`${authorizeUrl}?client_id=${clientId}&redirect_uri=${encodeURIComponent(
6767
redirectUri ?? `${origin}/api/auth/oauth/oidc`,
68-
)}&response_type=code&scope=openid+email+profile+offline_access${state ? `&state=${state}` : ''}`,
68+
)}&response_type=code&scope=openid+email+profile+offline_access${state ? `&state=${encodeURIComponent(state)}` : ''}`,
6969
user: async (accessToken: string, userInfoUrl: string) => {
7070
const res = await fetch(userInfoUrl, {
7171
headers: {

src/lib/oauth/withOAuth.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { NextApiReq, NextApiRes } from '@/lib/response';
22
import { OAuthProviderType } from '@prisma/client';
33
import { prisma } from '../db';
44
import { findProvider } from './providerUtil';
5-
import { createToken } from '../crypto';
5+
import { createToken, decrypt } from '../crypto';
66
import { config } from '../config';
77
import { User } from '../db/models/user';
88
import Logger, { log } from '../logger';
@@ -88,7 +88,14 @@ export const withOAuth =
8888

8989
const userOauth = findProvider(provider, user?.oauthProviders ?? []);
9090

91-
if (state === 'link') {
91+
let urlState;
92+
try {
93+
urlState = decrypt(decodeURIComponent(state ?? ''), config.core.secret);
94+
} catch {
95+
urlState = null;
96+
}
97+
98+
if (urlState === 'link') {
9299
if (!user) return res.unauthorized('invalid session');
93100

94101
if (findProvider(provider, user.oauthProviders))

src/pages/api/auth/oauth/discord.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import enabled from '@/lib/oauth/enabled';
66
import { discordAuth } from '@/lib/oauth/providerUtil';
77
import { fetchToDataURL } from '@/lib/base64';
88
import Logger from '@/lib/logger';
9+
import { encrypt } from '@/lib/crypto';
910

10-
async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
11+
async function handler({ code, host }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
1112
if (!config.features.oauthRegistration)
1213
return {
1314
error: 'OAuth registration is disabled.',
@@ -22,15 +23,18 @@ async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promi
2223
error_code: 401,
2324
};
2425

25-
if (!code)
26+
if (!code) {
27+
const linkState = encrypt('link', config.core.secret);
28+
2629
return {
2730
redirect: discordAuth.url(
2831
config.oauth.discord.clientId!,
2932
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
30-
state,
33+
linkState,
3134
config.oauth.discord.redirectUri ?? undefined,
3235
),
3336
};
37+
}
3438

3539
const body = new URLSearchParams({
3640
client_id: config.oauth.discord.clientId!,

src/pages/api/auth/oauth/github.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { fetchToDataURL } from '@/lib/base64';
22
import { config } from '@/lib/config';
3+
import { encrypt } from '@/lib/crypto';
34
import Logger from '@/lib/logger';
45
import { combine } from '@/lib/middleware/combine';
56
import { method } from '@/lib/middleware/method';
67
import enabled from '@/lib/oauth/enabled';
78
import { githubAuth } from '@/lib/oauth/providerUtil';
89
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
910

10-
async function handler({ code, state }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
11+
async function handler({ code }: OAuthQuery, logger: Logger): Promise<OAuthResponse> {
1112
if (!config.features.oauthRegistration)
1213
return {
1314
error: 'OAuth registration is disabled.',
@@ -22,14 +23,17 @@ async function handler({ code, state }: OAuthQuery, logger: Logger): Promise<OAu
2223
error_code: 401,
2324
};
2425

25-
if (!code)
26+
if (!code) {
27+
const linkState = encrypt('link', config.core.secret);
28+
2629
return {
2730
redirect: githubAuth.url(
2831
config.oauth.github.clientId!,
29-
state,
32+
linkState,
3033
config.oauth.github.redirectUri ?? undefined,
3134
),
3235
};
36+
}
3337

3438
const body = JSON.stringify({
3539
client_id: config.oauth.github.clientId!,

src/pages/api/auth/oauth/google.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { fetchToDataURL } from '@/lib/base64';
22
import { config } from '@/lib/config';
3+
import { encrypt } from '@/lib/crypto';
34
import Logger from '@/lib/logger';
45
import { combine } from '@/lib/middleware/combine';
56
import { method } from '@/lib/middleware/method';
67
import enabled from '@/lib/oauth/enabled';
78
import { googleAuth } from '@/lib/oauth/providerUtil';
89
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
910

10-
async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
11+
async function handler({ code, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
1112
if (!config.features.oauthRegistration)
1213
return {
1314
error: 'OAuth registration is disabled.',
@@ -22,15 +23,18 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
2223
error_code: 401,
2324
};
2425

25-
if (!code)
26+
if (!code) {
27+
const linkState = encrypt('link', config.core.secret);
28+
2629
return {
2730
redirect: googleAuth.url(
2831
config.oauth.google.clientId!,
2932
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
30-
state,
33+
linkState,
3134
config.oauth.google.redirectUri ?? undefined,
3235
),
3336
};
37+
}
3438

3539
const body = new URLSearchParams({
3640
client_id: config.oauth.google.clientId!,

src/pages/api/auth/oauth/oidc.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { config } from '@/lib/config';
2+
import { encrypt } from '@/lib/crypto';
23
import Logger from '@/lib/logger';
34
import { combine } from '@/lib/middleware/combine';
45
import { method } from '@/lib/middleware/method';
@@ -7,7 +8,7 @@ import { oidcAuth } from '@/lib/oauth/providerUtil';
78
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
89

910
// thanks to @danejur for this https://github.com/diced/zipline/pull/372
10-
async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
11+
async function handler({ code, host }: OAuthQuery, _logger: Logger): Promise<OAuthResponse> {
1112
if (!config.features.oauthRegistration)
1213
return {
1314
error: 'OAuth registration is disabled.',
@@ -22,16 +23,19 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
2223
error_code: 401,
2324
};
2425

25-
if (!code)
26+
if (!code) {
27+
const linkState = encrypt('link', config.core.secret);
28+
2629
return {
2730
redirect: oidcAuth.url(
2831
config.oauth.oidc.clientId!,
2932
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
3033
config.oauth.oidc.authorizeUrl!,
31-
state,
34+
linkState,
3235
config.oauth.oidc.redirectUri ?? undefined,
3336
),
3437
};
38+
}
3539

3640
const body = new URLSearchParams({
3741
client_id: config.oauth.oidc.clientId!,

0 commit comments

Comments
 (0)