- fixes #2153, attempt to fix 401 unauthorized error when implementing OIDC

This commit is contained in:
Amruth Pillai
2025-01-15 16:32:43 +01:00
parent 9a34e4af27
commit 63db927924
16 changed files with 219 additions and 196 deletions

View File

@ -75,6 +75,7 @@ export class AuthModule {
const clientID = configService.getOrThrow("OPENID_CLIENT_ID");
const clientSecret = configService.getOrThrow("OPENID_CLIENT_SECRET");
const issuer = configService.getOrThrow("OPENID_ISSUER");
const scope = configService.getOrThrow("OPENID_SCOPE");
const tokenURL = configService.getOrThrow("OPENID_TOKEN_URL");
const userInfoURL = configService.getOrThrow("OPENID_USER_INFO_URL");
@ -84,6 +85,7 @@ export class AuthModule {
clientID,
clientSecret,
issuer,
scope,
tokenURL,
userInfoURL,
userService,

View File

@ -201,12 +201,13 @@ export class AuthService {
if (
this.configService.get("OPENID_AUTHORIZATION_URL") &&
this.configService.get("OPENID_ISSUER") &&
this.configService.get("OPENID_TOKEN_URL") &&
this.configService.get("OPENID_USER_INFO_URL") &&
this.configService.get("OPENID_CALLBACK_URL") &&
this.configService.get("OPENID_CLIENT_ID") &&
this.configService.get("OPENID_CLIENT_SECRET") &&
this.configService.get("OPENID_CALLBACK_URL")
this.configService.get("OPENID_ISSUER") &&
this.configService.get("OPENID_SCOPE") &&
this.configService.get("OPENID_TOKEN_URL") &&
this.configService.get("OPENID_USER_INFO_URL")
) {
providers.push("openid");
}

View File

@ -1,5 +1,6 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { createId } from "@paralleldrive/cuid2";
import { User } from "@prisma/client";
import { ErrorMessage, processUsername } from "@reactive-resume/utils";
import { Profile, Strategy, StrategyOptions } from "passport-github2";
@ -46,15 +47,17 @@ export class GitHubStrategy extends PassportStrategy(Strategy, "github") {
email,
picture,
locale: "en-US",
name: displayName,
provider: "github",
name: displayName || createId(),
emailVerified: true, // auto-verify emails
username: processUsername(username ?? email.split("@")[0]),
secrets: { create: {} },
});
done(null, user);
} catch {
} catch (error) {
Logger.error(error);
throw new BadRequestException(ErrorMessage.UserAlreadyExists);
}
}

View File

@ -1,5 +1,6 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { createId } from "@paralleldrive/cuid2";
import { User } from "@prisma/client";
import { ErrorMessage, processUsername } from "@reactive-resume/utils";
import { Profile, Strategy, StrategyOptions, VerifyCallback } from "passport-google-oauth20";
@ -46,15 +47,17 @@ export class GoogleStrategy extends PassportStrategy(Strategy, "google") {
email,
picture,
locale: "en-US",
name: displayName,
provider: "google",
name: displayName || createId(),
emailVerified: true, // auto-verify emails
username: processUsername(username ?? email.split("@")[0]),
secrets: { create: {} },
});
done(null, user);
} catch {
} catch (error) {
Logger.error(error);
throw new BadRequestException(ErrorMessage.UserAlreadyExists);
}
}

View File

@ -1,7 +1,7 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { User } from "@prisma/client";
import { ErrorMessage, processUsername } from "@reactive-resume/utils";
import { ErrorMessage, generateRandomName, processUsername } from "@reactive-resume/utils";
import { Profile, Strategy, StrategyOptions } from "passport-openidconnect";
import { UserService } from "@/server/user/user.service";
@ -14,6 +14,7 @@ export class OpenIDStrategy extends PassportStrategy(Strategy, "openid") {
readonly clientID: string,
readonly clientSecret: string,
readonly issuer: string,
readonly scope: string,
readonly tokenURL: string,
readonly userInfoURL: string,
private readonly userService: UserService,
@ -24,20 +25,21 @@ export class OpenIDStrategy extends PassportStrategy(Strategy, "openid") {
clientID,
clientSecret,
issuer,
scope,
tokenURL,
userInfoURL,
scope: "openid email profile",
} as StrategyOptions);
}
async validate(
issuer: unknown,
_issuer: unknown,
profile: Profile,
done: (err?: string | Error | null, user?: Express.User, info?: unknown) => void,
) {
const { displayName, emails, photos, username } = profile;
const email = emails?.[0].value ?? `${username}@openid.com`;
const uniqueId = generateRandomName({ length: 2, style: "lowerCase", separator: "-" });
const email = emails?.[0].value ?? `${username ?? uniqueId}@openid.com`;
const picture = photos?.[0].value;
let user: User | null = null;
@ -58,15 +60,17 @@ export class OpenIDStrategy extends PassportStrategy(Strategy, "openid") {
email,
picture,
locale: "en-US",
name: displayName,
provider: "openid",
name: displayName || uniqueId,
emailVerified: true, // auto-verify emails
username: processUsername(username ?? email.split("@")[0]),
secrets: { create: {} },
});
done(null, user);
} catch {
} catch (error) {
Logger.error(error);
throw new BadRequestException(ErrorMessage.UserAlreadyExists);
}
}

View File

@ -74,11 +74,13 @@ export const configSchema = z.object({
GOOGLE_CALLBACK_URL: z.string().url().optional(),
// OpenID (Optional)
VITE_OPENID_NAME: z.string().optional(),
OPENID_AUTHORIZATION_URL: z.string().url().optional(),
OPENID_CALLBACK_URL: z.string().url().optional(),
OPENID_CLIENT_ID: z.string().optional(),
OPENID_CLIENT_SECRET: z.string().optional(),
OPENID_ISSUER: z.string().optional(),
OPENID_SCOPE: z.string().optional(),
OPENID_TOKEN_URL: z.string().url().optional(),
OPENID_USER_INFO_URL: z.string().url().optional(),
});