Skip to content

Commit

Permalink
feat: create apple login url
Browse files Browse the repository at this point in the history
  • Loading branch information
stae1102 committed Apr 14, 2024
1 parent a56fe29 commit ccd26c5
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 1 deletion.
40 changes: 39 additions & 1 deletion src/auth/oauth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Controller, Get, Res, Query, UseGuards } from '@nestjs/common';
import {
Controller,
Get,
Res,
Query,
UseGuards,
Post,
Body,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import {
ApiTags,
Expand Down Expand Up @@ -91,4 +99,34 @@ export class OAuthController {
): Promise<LoginOutput> {
return this.oauthService.googleOauth(user);
}

@ApiOperation({
summary: '애플 계정 로그인 요청',
description: '애플 계정 로그인 요청 메서드',
})
@Get('apple-auth')
async getAppleRedirectUrl(@Res() res: Response): Promise<void> {
const { url } = await this.oauthService.getAppleRedirectUrl();
return res.redirect(url);
}

@ApiOperation({
summary: '애플 로그인 리다이렉트 후 클라이언트 리다이렉트',
})
@Post('apple-code')
async appleCode(@Body('code') code: string, @Res() res: Response) {
return res.redirect(
`${process.env.APPLE_REDIRECT_FRONTEND_URI}?code=${code}`,
);
}

@ApiOperation({
summary: '애플 로그인',
description:
'애플 로그인 메서드. (회원가입이 안되어 있으면 회원가입 처리 후 로그인 처리)',
})
@Get('apple-login')
async appleLogin(@Query('code') code: string): Promise<LoginOutput> {
return this.oauthService.appleLogin(code);
}
}
51 changes: 51 additions & 0 deletions src/auth/oauth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Injectable,
UnauthorizedException,
BadRequestException,
InternalServerErrorException,
} from '@nestjs/common';
import axios from 'axios';
import { refreshTokenExpirationInCache } from './auth.module';
Expand Down Expand Up @@ -159,4 +160,54 @@ export class OAuthService {
secure: true,
};
}

public async getAppleRedirectUrl() {
const appleClientId = process.env.APPLE_CLIENT_ID;
const redirectUri = process.env.APPLE_REDIRECT_URI;

const config = {
client_id: appleClientId, // This is the service ID we created.
redirect_uri: redirectUri, // As registered along with our service ID
response_type: 'code id_token',
state: 'origin:web', // Any string of your choice that you may use for some logic. It's optional and you may omit it.
scope: 'name email', // To tell apple we want the user name and emails fields in the response it sends us.
response_mode: 'form_post',
m: 11,
v: '1.5.4',
};
const queryString = Object.entries(config)
.map(([key, value]) => `${key}=${encodeURIComponent(value!)}`)
.join('&');

return { url: `https://appleid.apple.com/auth/authorize?${queryString}` };
}

public async appleLogin(code: string) {
const { data } = await this.oauthUtil.getAppleToken(code);

if (!data.id_token) {
throw new InternalServerErrorException(
`No token: ${JSON.stringify(data)}`,
);
}

const { sub: id, email } = this.jwtService.verify(data.id_token);

let user = await this.userRepository.findOneByEmail(email);

if (!user) {
user = new User();
user.email = email;
user.name = email.split('@')[0];
user.password = this.encodePasswordFromEmail(
email,
process.env.APPLE_CLIENT_ID,
);

await this.userRepository.createOne(user);
await this.categoryRepository.createDefaultCategories(user);
}

return this.oauthLogin(user.email);
}
}
35 changes: 35 additions & 0 deletions src/auth/util/oauth.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import {
GetKakaoAccessTokenOutput,
GetKakaoUserInfoOutput,
} from '../dtos/kakao.dto';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class OAuthUtil {
constructor(private readonly jwtService: JwtService) {}

// Get access token from Kakao Auth Server
async getKakaoAccessToken(code: string): Promise<GetKakaoAccessTokenOutput> {
try {
Expand Down Expand Up @@ -68,4 +71,36 @@ export class OAuthUtil {
throw e;
}
}

getAppleAccessToken(): string {
return this.jwtService.sign(
{},
{
audience: 'https://appleid.apple.com',
issuer: process.env.APPLE_TEAM_ID,
subject: process.env.APPLE_CLIENT_ID,
expiresIn: '1h',
keyid: process.env.APPLE_KEY_ID,
algorithm: 'ES256',
},
);
}

async getAppleToken(code: string) {
return await axios.post(
'https://appleid.apple.com/auth/token',
qs.stringify({
grant_type: 'authorization_code',
code,
client_secret: this.getAppleAccessToken(),
client_id: process.env.APPLE_CLIENT_ID,
redirect_uri: process.env.APPLE_REDIRECT_URI,
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
}
}

0 comments on commit ccd26c5

Please sign in to comment.