fix(oauth): github and discord login error (#323)

* fix(oauth): github and discord login error
fixed #322, fixed #302

* feat(oauth): print log when ErrorPageException occurs

* refactor(oauth): migrate to Logger

* feat(oauth): add logger for OAuthExceptionFilter

* docs(oauth): update oauth login docs
This commit is contained in:
Qing Fu
2023-11-12 03:25:05 +08:00
committed by GitHub
parent 966ce261cb
commit fd44f42f28
5 changed files with 34 additions and 33 deletions

View File

@@ -1,12 +1,16 @@
import { ArgumentsHost, Catch, ExceptionFilter } from "@nestjs/common"; import { ArgumentsHost, Catch, ExceptionFilter, Logger } from "@nestjs/common";
import { ConfigService } from "../../config/config.service"; import { ConfigService } from "../../config/config.service";
import { ErrorPageException } from "../exceptions/errorPage.exception"; import { ErrorPageException } from "../exceptions/errorPage.exception";
@Catch(ErrorPageException) @Catch(ErrorPageException)
export class ErrorPageExceptionFilter implements ExceptionFilter { export class ErrorPageExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(ErrorPageExceptionFilter.name);
constructor(private config: ConfigService) {} constructor(private config: ConfigService) {}
catch(exception: ErrorPageException, host: ArgumentsHost) { catch(exception: ErrorPageException, host: ArgumentsHost) {
this.logger.error(exception);
const ctx = host.switchToHttp(); const ctx = host.switchToHttp();
const response = ctx.getResponse(); const response = ctx.getResponse();

View File

@@ -3,6 +3,7 @@ import {
Catch, Catch,
ExceptionFilter, ExceptionFilter,
HttpException, HttpException,
Logger,
} from "@nestjs/common"; } from "@nestjs/common";
import { ConfigService } from "../../config/config.service"; import { ConfigService } from "../../config/config.service";
@@ -12,14 +13,20 @@ export class OAuthExceptionFilter implements ExceptionFilter {
access_denied: "access_denied", access_denied: "access_denied",
expired_token: "expired_token", expired_token: "expired_token",
}; };
private readonly logger = new Logger(OAuthExceptionFilter.name);
constructor(private config: ConfigService) {} constructor(private config: ConfigService) {}
catch(_exception: HttpException, host: ArgumentsHost) { catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp(); const ctx = host.switchToHttp();
const response = ctx.getResponse(); const response = ctx.getResponse();
const request = ctx.getRequest(); const request = ctx.getRequest();
this.logger.error(exception.message);
this.logger.error(
"Request query: " + JSON.stringify(request.query, null, 2),
);
const key = this.errorKeys[request.query.error] || "default"; const key = this.errorKeys[request.query.error] || "default";
const url = new URL(`${this.config.get("general.appUrl")}/error`); const url = new URL(`${this.config.get("general.appUrl")}/error`);

View File

@@ -60,8 +60,8 @@ export class DiscordProvider implements OAuthProvider<DiscordToken> {
} }
async getUserInfo(token: OAuthToken<DiscordToken>): Promise<OAuthSignInDto> { async getUserInfo(token: OAuthToken<DiscordToken>): Promise<OAuthSignInDto> {
const res = await fetch("https://discord.com/api/v10/user/@me", { const res = await fetch("https://discord.com/api/v10/users/@me", {
method: "post", method: "get",
headers: { headers: {
Accept: "application/json", Accept: "application/json",
Authorization: `${token.tokenType || "Bearer"} ${token.accessToken}`, Authorization: `${token.tokenType || "Bearer"} ${token.accessToken}`,

View File

@@ -41,15 +41,16 @@ export class GitHubProvider implements OAuthProvider<GitHubToken> {
return { return {
accessToken: token.access_token, accessToken: token.access_token,
tokenType: token.token_type, tokenType: token.token_type,
scope: token.scope,
rawToken: token, rawToken: token,
}; };
} }
async getUserInfo(token: OAuthToken<GitHubToken>): Promise<OAuthSignInDto> { async getUserInfo(token: OAuthToken<GitHubToken>): Promise<OAuthSignInDto> {
const user = await this.getGitHubUser(token);
if (!token.scope.includes("user:email")) { if (!token.scope.includes("user:email")) {
throw new BadRequestException("No email permission granted"); throw new BadRequestException("No email permission granted");
} }
const user = await this.getGitHubUser(token);
const email = await this.getGitHubEmail(token); const email = await this.getGitHubEmail(token);
if (!email) { if (!email) {
throw new BadRequestException("No email found"); throw new BadRequestException("No email found");

View File

@@ -10,23 +10,22 @@
### GitHub ### GitHub
Please follow the [official guide](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) Please follow the [official guide](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) to create an OAuth app.
to create an OAuth app.
Redirect URL: `https://<your-domain>/api/oauth/callback/github` Redirect URL: `https://<your-domain>/api/oauth/callback/github`
### Google ### Google
Please follow the [official guide](https://developers.google.com/identity/protocols/oauth2/web-server#prerequisites) to Please follow the [official guide](https://developers.google.com/identity/protocols/oauth2/web-server#prerequisites) to create an OAuth 2.0 App.
create an OAuth 2.0 App.
Redirect URL: `https://<your-domain>/api/oauth/callback/google` Redirect URL: `https://<your-domain>/api/oauth/callback/google`
### Microsoft ### Microsoft
Please follow Please follow the [official guide](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) to register an application.
the [official guide](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app)
to register an application. > [!IMPORTANT]
> **Microsoft Tenant** you set in the admin panel must match the **supported account types** you set in the Microsoft Entra admin center, otherwise the OAuth login will not work. Refer to the [official documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc#find-your-apps-openid-configuration-document-uri) for more details.
Redirect URL: `https://<your-domain>/api/oauth/callback/microsoft` Redirect URL: `https://<your-domain>/api/oauth/callback/microsoft`
@@ -38,7 +37,7 @@ Redirect URL: `https://<your-domain>/api/oauth/callback/discord`
### OpenID Connect ### OpenID Connect
Generic OpenID Connect provider is also supported, we have tested it on Keycloak and Authentik. Generic OpenID Connect provider is also supported, we have tested it on Keycloak, Authentik and Casdoor.
Redirect URL: `https://<your-domain>/api/oauth/callback/oidc` Redirect URL: `https://<your-domain>/api/oauth/callback/oidc`
@@ -74,14 +73,11 @@ const configVariables: ConfigVariables = {
### 2. Create provider class ### 2. Create provider class
#### OpenID Connect #### Generic OpenID Connect
If your provider supports OpenID connect, it's extremely easy to If your provider supports OpenID connect, it's extremely easy to extend [`GenericOidcProvider`](../backend/src/oauth/provider/genericOidc.provider.ts) to add a new OpenID Connect provider.
extend [`GenericOidcProvider`](../backend/src/oauth/provider/genericOidc.provider.ts) to add a new OpenID Connect
provider.
The [Google provider](../backend/src/oauth/provider/google.provider.ts) The [Google provider](../backend/src/oauth/provider/google.provider.ts) and [Microsoft provider](../backend/src/oauth/provider/microsoft.provider.ts) are good examples.
and [Microsoft provider](../backend/src/oauth/provider/microsoft.provider.ts) are good examples.
Here are some discovery URIs for popular providers: Here are some discovery URIs for popular providers:
@@ -95,17 +91,13 @@ Here are some discovery URIs for popular providers:
#### OAuth 2 #### OAuth 2
If your provider only supports OAuth 2, you can If your provider only supports OAuth 2, you can implement [`OAuthProvider`](../backend/src/oauth/provider/oauthProvider.interface.ts) interface to add a new OAuth 2 provider.
implement [`OAuthProvider`](../backend/src/oauth/provider/oauthProvider.interface.ts) interface to add a new OAuth 2
provider.
The [GitHub provider](../backend/src/oauth/provider/github.provider.ts) The [GitHub provider](../backend/src/oauth/provider/github.provider.ts) and [Discord provider](../backend/src/oauth/provider/discord.provider.ts) are good examples.
and [Discord provider](../backend/src/oauth/provider/discord.provider.ts) are good examples.
### 3. Register provider ### 3. Register provider
Register your provider in [`OAuthModule`](../backend/src/oauth/oauth.module.ts) Register your provider in [`OAuthModule`](../backend/src/oauth/oauth.module.ts) and [`OAuthSignInDto`](../backend/src/oauth/dto/oauthSignIn.dto.ts):
and [`OAuthSignInDto`](../backend/src/oauth/dto/oauthSignIn.dto.ts):
```ts ```ts
@Module({ @Module({
@@ -117,8 +109,7 @@ and [`OAuthSignInDto`](../backend/src/oauth/dto/oauthSignIn.dto.ts):
useFactory(github: GitHubProvider, /* your provider */): Record<string, OAuthProvider<unknown>> { useFactory(github: GitHubProvider, /* your provider */): Record<string, OAuthProvider<unknown>> {
return { return {
github, github,
google, /* your provider */
oidc,
}; };
}, },
inject: [GitHubProvider, /* your provider */], inject: [GitHubProvider, /* your provider */],
@@ -131,8 +122,7 @@ export class OAuthModule {
```ts ```ts
export interface OAuthSignInDto { export interface OAuthSignInDto {
provider: 'github' | 'google' | 'microsoft' | 'discord' | 'oidc' /* your provider*/ provider: 'github' | 'google' | 'microsoft' | 'discord' | 'oidc' /* your provider*/;
;
providerId: string; providerId: string;
providerUsername: string; providerUsername: string;
email: string; email: string;
@@ -161,8 +151,7 @@ Add keys below to your i18n text in [locale file](../frontend/src/i18n/translati
- `admin.config.oauth.YOUR_PROVIDER_NAME-enabled` - `admin.config.oauth.YOUR_PROVIDER_NAME-enabled`
- `admin.config.oauth.YOUR_PROVIDER_NAME-client-id` - `admin.config.oauth.YOUR_PROVIDER_NAME-client-id`
- `admin.config.oauth.YOUR_PROVIDER_NAME-client-secret` - `admin.config.oauth.YOUR_PROVIDER_NAME-client-secret`
- `error.param.provider_YOUR_PROVIDER_NAME`
- Other config keys you defined in step 1 - Other config keys you defined in step 1
Congratulations! 🎉 You have successfully added a new OAuth 2 provider! Pull requests are welcome if you want to share Congratulations! 🎉 You have successfully added a new OAuth 2 provider! Pull requests are welcome if you want to share your provider with others.
your provider with others.