mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 13:25:12 +00:00
feat(server): add invalid oauth callback code error handling (#10603)
close CLOUD-130
This commit is contained in:
@@ -301,6 +301,12 @@ export const USER_FRIENDLY_ERRORS = {
|
||||
type: 'bad_request',
|
||||
message: 'Invalid callback state parameter.',
|
||||
},
|
||||
invalid_oauth_callback_code: {
|
||||
type: 'bad_request',
|
||||
args: { status: 'number', body: 'string' },
|
||||
message: ({ status, body }) =>
|
||||
`Invalid callback code parameter, provider response status: ${status} and body: ${body}.`,
|
||||
},
|
||||
missing_oauth_query_parameter: {
|
||||
type: 'bad_request',
|
||||
args: { name: 'string' },
|
||||
|
||||
@@ -105,6 +105,17 @@ export class InvalidOauthCallbackState extends UserFriendlyError {
|
||||
}
|
||||
}
|
||||
@ObjectType()
|
||||
class InvalidOauthCallbackCodeDataType {
|
||||
@Field() status!: number
|
||||
@Field() body!: string
|
||||
}
|
||||
|
||||
export class InvalidOauthCallbackCode extends UserFriendlyError {
|
||||
constructor(args: InvalidOauthCallbackCodeDataType, message?: string | ((args: InvalidOauthCallbackCodeDataType) => string)) {
|
||||
super('bad_request', 'invalid_oauth_callback_code', message, args);
|
||||
}
|
||||
}
|
||||
@ObjectType()
|
||||
class MissingOauthQueryParameterDataType {
|
||||
@Field() name!: string
|
||||
}
|
||||
@@ -820,6 +831,7 @@ export enum ErrorNames {
|
||||
UNKNOWN_OAUTH_PROVIDER,
|
||||
OAUTH_STATE_EXPIRED,
|
||||
INVALID_OAUTH_CALLBACK_STATE,
|
||||
INVALID_OAUTH_CALLBACK_CODE,
|
||||
MISSING_OAUTH_QUERY_PARAMETER,
|
||||
OAUTH_ACCOUNT_ALREADY_CONNECTED,
|
||||
INVALID_EMAIL,
|
||||
@@ -916,5 +928,5 @@ registerEnumType(ErrorNames, {
|
||||
export const ErrorDataUnionType = createUnionType({
|
||||
name: 'ErrorDataUnion',
|
||||
types: () =>
|
||||
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocActionDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType, UnsupportedClientVersionDataType] as const,
|
||||
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, InvalidOauthCallbackCodeDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocActionDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType, UnsupportedClientVersionDataType] as const,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,9 @@ import {
|
||||
Controller,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Post,
|
||||
type RawBodyRequest,
|
||||
Req,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
@@ -27,6 +29,8 @@ import { OAuthService } from './service';
|
||||
|
||||
@Controller('/api/oauth')
|
||||
export class OAuthController {
|
||||
private readonly logger = new Logger(OAuthController.name);
|
||||
|
||||
constructor(
|
||||
private readonly auth: AuthService,
|
||||
private readonly oauth: OAuthService,
|
||||
@@ -69,7 +73,7 @@ export class OAuthController {
|
||||
@Post('/callback')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async callback(
|
||||
@Req() req: Request,
|
||||
@Req() req: RawBodyRequest<Request>,
|
||||
@Res() res: Response,
|
||||
@Body('code') code?: string,
|
||||
@Body('state') stateStr?: string
|
||||
@@ -102,7 +106,20 @@ export class OAuthController {
|
||||
throw new UnknownOauthProvider({ name: state.provider ?? 'unknown' });
|
||||
}
|
||||
|
||||
const tokens = await provider.getToken(code);
|
||||
let tokens: Tokens;
|
||||
try {
|
||||
tokens = await provider.getToken(code);
|
||||
} catch (err) {
|
||||
let rayBodyString = '';
|
||||
if (req.rawBody) {
|
||||
// only log the first 4096 bytes of the raw body
|
||||
rayBodyString = req.rawBody.subarray(0, 4096).toString('utf-8');
|
||||
}
|
||||
this.logger.warn(
|
||||
`Error getting oauth token for ${state.provider}, callback code: ${code}, stateStr: ${stateStr}, rawBody: ${rayBodyString}, error: ${err}`
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
const externAccount = await provider.getUser(tokens.accessToken);
|
||||
const user = await this.loginFromOauth(
|
||||
state.provider,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Config, URLHelper } from '../../../base';
|
||||
import { Config, InvalidOauthCallbackCode, URLHelper } from '../../../base';
|
||||
import { OAuthProviderName } from '../config';
|
||||
import { AutoRegisteredOAuthProvider } from '../register';
|
||||
|
||||
@@ -64,10 +64,12 @@ export class GithubOAuthProvider extends AutoRegisteredOAuthProvider {
|
||||
scope: ghToken.scope,
|
||||
};
|
||||
} else {
|
||||
const body = await response.text();
|
||||
if (response.status < 500) {
|
||||
throw new InvalidOauthCallbackCode({ status: response.status, body });
|
||||
}
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
}, ${JSON.stringify(await response.json())}`
|
||||
`Server responded with non-success status ${response.status}, body: ${body}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Config, URLHelper } from '../../../base';
|
||||
import { Config, InvalidOauthCallbackCode, URLHelper } from '../../../base';
|
||||
import { OAuthProviderName } from '../config';
|
||||
import { AutoRegisteredOAuthProvider } from '../register';
|
||||
|
||||
@@ -69,10 +69,12 @@ export class GoogleOAuthProvider extends AutoRegisteredOAuthProvider {
|
||||
scope: ghToken.scope,
|
||||
};
|
||||
} else {
|
||||
const body = await response.text();
|
||||
if (response.status < 500) {
|
||||
throw new InvalidOauthCallbackCode({ status: response.status, body });
|
||||
}
|
||||
throw new Error(
|
||||
`Server responded with non-success code ${
|
||||
response.status
|
||||
}, ${JSON.stringify(await response.json())}`
|
||||
`Server responded with non-success status ${response.status}, body: ${body}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ type EditorType {
|
||||
name: String!
|
||||
}
|
||||
|
||||
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
||||
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidOauthCallbackCodeDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
||||
|
||||
enum ErrorNames {
|
||||
ACCESS_DENIED
|
||||
@@ -382,6 +382,7 @@ enum ErrorNames {
|
||||
INVALID_LICENSE_SESSION_ID
|
||||
INVALID_LICENSE_TO_ACTIVATE
|
||||
INVALID_LICENSE_UPDATE_PARAMS
|
||||
INVALID_OAUTH_CALLBACK_CODE
|
||||
INVALID_OAUTH_CALLBACK_STATE
|
||||
INVALID_PASSWORD_LENGTH
|
||||
INVALID_RUNTIME_CONFIG_TYPE
|
||||
@@ -503,6 +504,11 @@ type InvalidLicenseUpdateParamsDataType {
|
||||
reason: String!
|
||||
}
|
||||
|
||||
type InvalidOauthCallbackCodeDataType {
|
||||
body: String!
|
||||
status: Int!
|
||||
}
|
||||
|
||||
type InvalidPasswordLengthDataType {
|
||||
max: Int!
|
||||
min: Int!
|
||||
|
||||
Reference in New Issue
Block a user