fix(gateway): accept Kakao already-registered response
This commit is contained in:
@@ -25,6 +25,11 @@ export interface KakaoUserInfo {
|
|||||||
kakaoAccount: KakaoAccountInfo;
|
kakaoAccount: KakaoAccountInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface KakaoSignupResult {
|
||||||
|
id?: string;
|
||||||
|
alreadyRegistered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const buildForm = (params: Record<string, string>): URLSearchParams => {
|
const buildForm = (params: Record<string, string>): URLSearchParams => {
|
||||||
const form = new URLSearchParams();
|
const form = new URLSearchParams();
|
||||||
for (const [key, value] of Object.entries(params)) {
|
for (const [key, value] of Object.entries(params)) {
|
||||||
@@ -106,19 +111,24 @@ export class KakaoOAuthClient {
|
|||||||
return parseToken(payload);
|
return parseToken(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
async signup(accessToken: string): Promise<{ id?: string; msg?: string }> {
|
async signup(accessToken: string): Promise<KakaoSignupResult> {
|
||||||
const response = await fetch(new URL('/v1/user/signup', this.apiHost), {
|
const response = await fetch(new URL('/v1/user/signup', this.apiHost), {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const payload = (await response.json()) as Record<string, unknown>;
|
const payload = (await response.json()) as Record<string, unknown>;
|
||||||
|
if (!response.ok && payload.code === -102 && payload.msg === 'already registered') {
|
||||||
|
return {
|
||||||
|
alreadyRegistered: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Kakao signup error: ${JSON.stringify(payload)}`);
|
throw new Error(`Kakao signup error: ${JSON.stringify(payload)}`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: payload.id ? String(payload.id) : undefined,
|
id: payload.id ? String(payload.id) : undefined,
|
||||||
msg: payload.msg ? String(payload.msg) : undefined,
|
alreadyRegistered: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ export const appRouter = router({
|
|||||||
const tokenIssuedAt = new Date();
|
const tokenIssuedAt = new Date();
|
||||||
|
|
||||||
const signupResult = await ctx.kakaoClient.signup(token.accessToken);
|
const signupResult = await ctx.kakaoClient.signup(token.accessToken);
|
||||||
const alreadyRegisteredWithKakao = !signupResult.id && signupResult.msg === 'already registered';
|
const alreadyRegisteredWithKakao = !signupResult.id && signupResult.alreadyRegistered;
|
||||||
if (!signupResult.id && !alreadyRegisteredWithKakao) {
|
if (!signupResult.id && !alreadyRegisteredWithKakao) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'BAD_REQUEST',
|
code: 'BAD_REQUEST',
|
||||||
|
|||||||
@@ -71,7 +71,9 @@ const buildCaller = (
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
signup: async () =>
|
signup: async () =>
|
||||||
options.kakaoSignupAlreadyRegistered ? { msg: 'already registered' as const } : { id: kakaoProfile.id },
|
options.kakaoSignupAlreadyRegistered
|
||||||
|
? { alreadyRegistered: true }
|
||||||
|
: { id: kakaoProfile.id, alreadyRegistered: false },
|
||||||
getMe: async () => ({
|
getMe: async () => ({
|
||||||
id: kakaoProfile.id,
|
id: kakaoProfile.id,
|
||||||
kakaoAccount: {
|
kakaoAccount: {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { KakaoOAuthClient } from '../src/auth/kakaoClient.js';
|
||||||
|
|
||||||
|
const createClient = (): KakaoOAuthClient =>
|
||||||
|
new KakaoOAuthClient({
|
||||||
|
restKey: 'rest-key',
|
||||||
|
redirectUri: 'https://gateway.example.test/oauth/callback',
|
||||||
|
apiHost: 'https://kapi.example.test',
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Kakao OAuth HTTP transport', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Kakao -102 already registered errors into an account recovery result', async () => {
|
||||||
|
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify({ msg: 'already registered', code: -102 }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(createClient().signup('access-token')).resolves.toEqual({
|
||||||
|
alreadyRegistered: true,
|
||||||
|
});
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith(new URL('https://kapi.example.test/v1/user/signup'), {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer access-token',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('continues to reject unrelated Kakao signup errors', async () => {
|
||||||
|
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify({ msg: 'invalid request', code: -201 }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(createClient().signup('access-token')).rejects.toThrow(
|
||||||
|
'Kakao signup error: {"msg":"invalid request","code":-201}'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user