์ ํ ๋ก๊ทธ์ธ์ ๊ตฌํํ๋ ค๊ณ ํ ๋ ํ์ํ Backend ๋ก์ง์ ๋ํด์ ์ ๋ฆฌํ๋ ค๊ณ ํฉ๋๋ค. ํ์๋ ์ด๋ฒ์ ์ฒ์ ๊ตฌ์ฑํ๋ ๊ฒ์ ๋ํ ์ ๋ฆฌ์ด๋ค ๋ณด๋, ์ ํ์์ ์ํ๋ ํน์ ์๋ํ ๊ฒ์ฆ ๋ฐฉ์์ ์ถฉ์คํ ๋ฐ๋ฅด์ง ๋ชปํ ์ ์์ต๋๋ค. ์ด๋๊น์ง๋ ์ฐธ๊ณ ์ฉ์ผ๋ก ๋ด์ฃผ์๊ณ , ์๋ฌธ์ ์ด๋ ํผ๋๋ฐฑ์ด ์์ผ์๋ฉด ๋๊ธ์ ๋จ๊ฒจ์ฃผ์ธ์! ๊ฐ์ด ๊ณ ๋ฏผํด๋ณด๋ฉด ๋ ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
2๊ฐ์ง ๋ฐฉ๋ฒ
์ ํ ๋ก๊ทธ์ธ์ ๊ตฌํํ๊ธฐ ์ํด์๋ ํฌ๊ฒ 2๊ฐ์ง ๋ฐฉ๋ฒ์ ์ทจํ ์ ์๋ค.
- Frontend์์ ๋ชจ๋ ์ ์ ์ ๋ณด๊ฐ ๋ด๊ธด
id_token
์ ๋ฐ์์, ํด๋นid_token
์ด ์ ํจํ์ง ๊ฒ์ฆํ๊ธฐ - Frontend์์ Authorization์ ์ํ
code
๋ฅผ ๋ฐ์์, ํด๋นcode
๋ก ์ ํ ์๋ฒ์์token
๊ณผrefresh_token
๋ฐ๊ธฐ
ํน์ 1๊ณผ 2๋ฅผ ๋์์ ์ฌ์ฉํ์ฌ ์กฐ๊ธ ๋ ์๊ฒฉํ ์ธ์ฆ ๊ด๋ฆฌ๋ ๊ฐ๋ฅํ๊ฒ ๋ค.
์ด๋ฒ ๊ธ์์๋ ๊ฐ๊ฐ์ ๋ฐฉ๋ฒ์ ์ด๋ป๊ฒ ๊ตฌํํ๋ฉด ๋๋์ง, ๊ทธ๋ฆฌ๊ณ ์ ์ด ์ธ์ฆ ๋ฐฉ์์ด ์ ํจํ์ง์ ๋ํด ์์๋ณด๋๋ก ํ๊ฒ ๋ค. (์์ ์ฝ๋๋ TypeScript๋ก ์์ฑํ๋๋ก ํ๊ฒ ๋ค.)
id_token ๊ฒ์ฆ
์ด ๋ฐฉ๋ฒ์ ์ ํ ๋ก๊ทธ์ธ์ ๊ตฌํํ๋ ์ง๊ด์ ์ด๊ณ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
์ด ๊ฒ์ฆ ๋ฐฉ์์ Sequence Diagram์ ๋ค์๊ณผ ๊ฐ๋ค.
์ด ๋ฐฉ์์ ํต์ฌ์ Client์์ API ์๋ฒ๋ก ๊ฑด๋ค์ฃผ๋ JWT ํ ํฐ์ธ id_token
์ payload ํํธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ํ ์ ๋ณด(sub
, iat
, exp
๋ฑ)๋ ๋ชจ๋ ํฌํจ๋์ด ์๋ค๋ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ ์ ํ ์๋ฒ์ ๋ณ๋์ ์์ฒญ์ ํ ํ์๊ฐ ์๋ค. ๊ทธ์ ์ด ํ ํฐ์ด ์ ํจํ์ง, ์๋๋ฉด API ์๋ฒ์ ๋๋ฌํ๊ธฐ ์ ์ ๋ณ์กฐ๋์ง ์์๋์ง ํ์ธํ๋ฉด ๋๋ ๊ฒ์ด๋ค.
์ด๋ฅผ ์ํํ๊ธฐ ์ํ Function์ ๊ฐ๋จํ ์์ฑํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค. (๋ก์ง๋ง ๋ณด๊ธฐ ์ํด์ ์์ธ ์ฒ๋ฆฌ ๋ถ๋ถ์ ์ ๊ฑฐํ์๋ค.)
import * as jwt from 'jsonwebtoken';
import { JwksClient } from 'jwks-rsa';
interface AppleJwtTokenPayload {
iss: string;
aud: string;
exp: number;
iat: number;
sub: string;
nonce: string;
c_hash: string;
email?: string;
email_verified?: string;
is_private_email?: string;
auth_time: number;
nonce_supported: boolean;
}
async verifyAppleToken(appleIdToken: string): Promise<AppleJwtTokenPayload> {
const decodedToken = jwt.decode(appleIdToken, { complete: true }) as {
header: { kid: string; alg: jwt.Algorithm };
payload: { sub: string };
};
const keyIdFromToken = decodedToken.header.kid;
const applePublicKeyUrl = 'https://appleid.apple.com/auth/keys';
const jwksClient = new JwksClient({ jwksUri: applePublicKeyUrl });
const key = await jwksClient.getSigningKey(keyIdFromToken);
const publicKey = key.getPublicKey();
const verifiedDecodedToken: AppleJwtTokenPayload = jwt.verify(appleIdToken, publicKey, {
algorithms: [decodedToken.header.alg]
}) as AppleJwtTokenPayload;
return verifiedDecodedToken;
}
Function์ ์๋ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
id_token
(JWT)์ decode ํด์ผ ํ๋ค. ์ด ๋,complete
์ต์ ์true
๋ก ์ค์ ํ์ฌheader
๊น์ง ์ป์ ์ ์๋ ํํ๋ก decode ํ๋ค.- ์ ํ์
id_token
์header
์๋ key ID์ธkid
๊ฐ ํฌํจ๋์ด ์๋ค. ์ด๊ฒ์ด ํ์ํ๋ค. - ์ ํ์ Public Key URL์์ JWKS(Json Web Key Set)์ ๊ฐ์ ธ์จ๋ค.
- ์ด JWKS์๋ ์ฐ๋ฆฌ๊ฐ
header
์์ ๊บผ๋ด์จkid
์ ๋์๋๋ Key Set์ด ๋ฐ๋์ ์กด์ฌํด์ผ ํ๋ค. ์ด Key Set์ผ๋ก๋ถํฐsigningKey
๋ฅผ ๊ฐ์ ธ์จ๋ค. - ํด๋น
signingKey
์์publicKey
๋ฅผ ์ถ์ถํ๋ค. - ์ด์
publicKey
๋ฅผ ์ด์ฉํ์ฌid_token
์ ๊ฒ์ฆํ๋ค. ์ด ๋ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์header
์ ๋ค์ด์์๋ ์๊ณ ๋ฆฌ์ฆ ์ ๋ณด๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ค. - ๊ฒ์ฆ๋ ํ ํฐ์ payload๋ฅผ ๋ฐํํ๋ค.
ํฌ๊ฒ ์ด๋ ค์ด ๋ถ๋ถ ์์ด Sequence Diagram์ ์ถฉ์คํ ๋ฐ๋ฅด๊ณ ์๋ Function์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
id_token
์ Signature๋ ์ ํ์ Private Key๋ก ์ํธํ ๋์ด ์๋ ์ํ์ด๊ณ , ์ด๊ฒ์ ์ ํ์ Public Key๋ก๋ง ๋ณตํธํ๊ฐ ๊ฐ๋ฅํ๋ค. ๋ค๋ฅด๊ฒ ๋งํด์, ์ ํ์ Private Key๊ฐ ์๋ ์์์ Private Key๋ก ์ํธํ ๋์์ ๊ฒฝ์ฐ, ์ ํ์ Public Key๋ก ๋ณตํธํ๊ฐ ๋ถ๊ฐ๋ฅ ํ ๊ฒ์ด๋ค.
์ด๋ฅผ ํตํด์ id_token
์ด ์ค๊ฐ์ ๋ณ์กฐ ๋์๋์ง, ์๋๋ฉด ๋คํธ์ํฌ ์ ์ก ๊ณผ์ ์์ ์์ ๋์๋์ง ๊ฒ์ฆํ ์ ์๋ค.
code ๊ฒ์ฆ
์ด ๋ฐฉ๋ฒ์ ์ ์ฝ ์ฌํญ๋ ๋ช ๊ฐ์ง ๋ ์๊ธฐ๊ณ ์ ํ ์๋ฒ์ ์์ฒญ๋ ์ถ๊ฐ์ ์ผ๋ก ๋ณด๋ด์ผ ํ๋ ๋จ์ ์ด ์์ง๋ง, ์ ํ๋ก๋ถํฐ ์ฐ๋ฆฌ์ API ์๋ฒ์ ์งํต์ผ๋ก ํ ํฐ์ ์ ๊ณต๋ฐ๊ธฐ ๋๋ฌธ์ ํด๋น ํ ํฐ์ 100% ์ ๋ขฐํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค. ๋ํ, ํ ๋ฒ Refresh Token์ ์ ๊ณต ๋ฐ๊ณ , ์ด๋ฅผ ์ ๊ด๋ฆฌํ๋ฉด ์ ์ ๊ฐ ์ถ๊ฐ์ ์ผ๋ก ์ ํ ๋ก๊ทธ์ธ ๋ชจ๋ฌ์ ๋ณด์ง ์์๋ ๋๋ค๋ ์ฅ์ ์ด ์๋ค.
์ด ๊ฒ์ฆ ๋ฐฉ์์ Sequence Diagram์ ๋ค์๊ณผ ๊ฐ๋ค.
์ด ๋ฐฉ๋ฒ์ ์ํํ๊ธฐ ์ํด์๋ ์ ํ ๊ฐ๋ฐ์ ์ฝ์์์ .p8
ํ์ฅ์๋ฅผ ๊ฐ์ง Private Key๋ฅผ ์์ฑํด์ผ ํ๋ค. (์ด์ ๋ํ ์ ๋ฐ์ ์ธ ์ ์ฐจ๋ https://github.com/ananay/apple-auth/blob/master/SETUP.md ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ๋๋ค.)
์ด ๊ฒ์ฆ ๋ฐฉ์์ ๋ํ ์ฝ๋๋ https://github.com/ananay/apple-auth ๋ฅผ ์ฐธ๊ณ ํ๊ฑฐ๋, ์ฌ์ฉํ๋ฉด ๋๋ค.
Passport ์ฌ์ฉ์๋ค์ ๊ฒฝ์ฐ https://github.com/ananay/passport-apple ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋, NestJs์์์ ๋์์ ์ฝ๊ฐ์ ์์ ์ ์ํ๋ค๋ Issue๋ค์ด ์๋ค.
๋ญ๊ฐ ๋ง๋ ๋ฐฉ๋ฒ์ผ๊น
์ด๋ค ๊ฒ์ด ์ณ์ ๋ฐฉ๋ฒ์ธ์ง, ํน์ ์ ํ์์ ๊ถ์ฅํ๋ ๋ฐฉ๋ฒ์ด ๋ฌด์์ธ์ง์ ๋ํด์ ์์ง๋ ๊ฐ๋ ์ ์ผ๋ก ์ ๋ฆฌํ์ง๋ ๋ชปํ ๊ฒ ๊ฐ๋ค. 2022๋ WWDC๋ฅผ ๊ธฐ์ ์ผ๋ก ์ ํ ๊ฐ๋ฐ์ ๋ฌธ์์ SideBar๊ฐ ์๊ฒจ์ ์๋นํ ์ฝ๊ธฐ ํธํด์ก๋๋ฐ, ๋ค์ ์ฝ์ด๋ณด๋ฉด์ ์ ํ ๋ก๊ทธ์ธ ๊ฒ์ฆ์ ๋ํ ์๋๋ฅผ ํ์ ํด๋ด์ผ ํ ๊ฒ ๊ฐ๋ค.
๋.