mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
fix: apple sign (#14053)
This commit is contained in:
@@ -4,7 +4,7 @@ import { defineModuleConfig, JSONSchema } from '../../base';
|
|||||||
|
|
||||||
export interface OAuthProviderConfig {
|
export interface OAuthProviderConfig {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret: string;
|
clientSecret?: string;
|
||||||
args?: Record<string, string>;
|
args?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { JsonWebKey } from 'node:crypto';
|
|||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import jwt, { type JwtPayload } from 'jsonwebtoken';
|
import jwt, { type JwtPayload } from 'jsonwebtoken';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
@@ -19,14 +20,75 @@ interface AuthTokenResponse {
|
|||||||
expires_in: number;
|
expires_in: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AppleProviderArgsSchema = z.object({
|
||||||
|
privateKey: z.string().nonempty(),
|
||||||
|
keyId: z.string().nonempty(),
|
||||||
|
teamId: z.string().nonempty(),
|
||||||
|
});
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppleOAuthProvider extends OAuthProvider {
|
export class AppleOAuthProvider extends OAuthProvider {
|
||||||
provider = OAuthProviderName.Apple;
|
provider = OAuthProviderName.Apple;
|
||||||
|
private args: z.infer<typeof AppleProviderArgsSchema> | null = null;
|
||||||
|
private _jwtCache: { token: string; expiresAt: number } | null = null;
|
||||||
|
|
||||||
constructor(private readonly url: URLHelper) {
|
constructor(private readonly url: URLHelper) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override get configured() {
|
||||||
|
if (this.config && !this.args) {
|
||||||
|
const result = AppleProviderArgsSchema.safeParse(this.config?.args);
|
||||||
|
if (result.success) {
|
||||||
|
this.args = result.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
!!this.config &&
|
||||||
|
!!this.config.clientId &&
|
||||||
|
(!!this.config.clientSecret || !!this.args)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get clientSecret() {
|
||||||
|
if (this.config.clientSecret) {
|
||||||
|
return this.config.clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.args) {
|
||||||
|
throw new Error('Missing Apple OAuth configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._jwtCache && this._jwtCache.expiresAt > Date.now()) {
|
||||||
|
return this._jwtCache.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { privateKey, keyId, teamId } = this.args;
|
||||||
|
const expiresIn = 300; // 5 minutes
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = jwt.sign({}, privateKey, {
|
||||||
|
algorithm: 'ES256',
|
||||||
|
keyid: keyId,
|
||||||
|
expiresIn,
|
||||||
|
issuer: teamId,
|
||||||
|
audience: 'https://appleid.apple.com',
|
||||||
|
subject: this.config.clientId,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._jwtCache = {
|
||||||
|
token,
|
||||||
|
expiresAt: Date.now() + (expiresIn - 30) * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
return token;
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error('Failed to generate Apple client secret JWT', e);
|
||||||
|
throw new Error('Failed to generate client secret');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getAuthUrl(state: string, clientNonce?: string): string {
|
getAuthUrl(state: string, clientNonce?: string): string {
|
||||||
return `https://appleid.apple.com/auth/authorize?${this.url.stringify({
|
return `https://appleid.apple.com/auth/authorize?${this.url.stringify({
|
||||||
client_id: this.config.clientId,
|
client_id: this.config.clientId,
|
||||||
@@ -46,7 +108,7 @@ export class AppleOAuthProvider extends OAuthProvider {
|
|||||||
body: this.url.stringify({
|
body: this.url.stringify({
|
||||||
code,
|
code,
|
||||||
client_id: this.config.clientId,
|
client_id: this.config.clientId,
|
||||||
client_secret: this.config.clientSecret,
|
client_secret: this.clientSecret,
|
||||||
redirect_uri: this.url.link('/api/oauth/callback'),
|
redirect_uri: this.url.link('/api/oauth/callback'),
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user