Skip to content

Commit

Permalink
feat: Apple OAuth Login을 추가한다. (#80)
Browse files Browse the repository at this point in the history
* chore: remove unused util class

* feat: OAuthProvider에 Apple을 추가한다.

* feat: bouncy castle, JWT decode 라이브러리를 추가한다.

* feat: yml에 apple 환경 변수를 추가한다.

* feat: apple login service를 구현한다.

* refactor: Apple login 시 name을 http body로 받는다.

* feat: OAuth 계정을 revoke하는 메소드를 추가한다.

* feat: 최초 로그인 시 authorizationCode로 refreshToken을 취득한다.

* feat: AppleUser 정보를 저장하는 Entity를 추가한다.

* feat: 회원가입 시 Apple User인 경우 AppleUser를 저장한다.
  • Loading branch information
rlarltj authored May 9, 2024
1 parent 4f9bc09 commit 4af195a
Show file tree
Hide file tree
Showing 24 changed files with 326 additions and 48 deletions.
4 changes: 4 additions & 0 deletions .docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ services:
DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
REDIS_HOST: ${REDIS_HOST}
REDIS_PORT: ${REDIS_PORT}
APPLE_PRIVATE_KEY: ${APPLE_PRIVATE_KEY}
APPLE_TEAM_ID: ${APPLE_TEAM_ID}
APPLE_KEY_ID: ${APPLE_KEY_ID}
APPLE_CLIENT_ID: ${APPLE_CLIENT_ID}

redis:
container_name: moneymong-redis
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches:
- dev

- feat/**
# 권한 설정
permissions:
contents: read
Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'org.bouncycastle:bcprov-jdk18on:1.75'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.75'
implementation "com.auth0:java-jwt:4.4.0"

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public class LoginRequest {
@NotBlank
private String provider;

@NotBlank
private String accessToken;

private String name;

private String code;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.moneymong.domain.user.api.request;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;

@Getter
public class UserDeleteRequest {
@NotBlank
private String provider;

@NotBlank
private String token;
}
37 changes: 37 additions & 0 deletions src/main/java/com/moneymong/domain/user/entity/AppleUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.moneymong.domain.user.entity;

import com.moneymong.global.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

import static lombok.AccessLevel.PROTECTED;

@Table(name = "apple_users")
@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = PROTECTED)
@Where(clause = "deleted = false")
@SQLDelete(sql = "UPDATE users SET deleted = true where id=?")
public class AppleUser extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private Long userId;

private String appleRefreshToken;

public static AppleUser of(Long userId, String appleRefreshToken) {
return AppleUser.builder()
.userId(userId)
.appleRefreshToken(appleRefreshToken)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.util.Assert;

import static com.moneymong.utils.TextValidator.checkText;
import static lombok.AccessLevel.PROTECTED;

@Table(name = "user_universities")
Expand Down Expand Up @@ -45,14 +45,14 @@ public class UserUniversity extends TimeBaseEntity {
private int grade;

public void update(String universityName, int grade) {
checkText(universityName, "대학 이름은 필수 입력값입니다.");
Assert.hasText(universityName, "대학 이름은 필수 입력값입니다.");

this.universityName = universityName;
this.grade = grade;
}

public static UserUniversity of(Long userId, String universityName, int grade) {
checkText(universityName, "대학 이름은 필수 입력값입니다.");
Assert.hasText(universityName, "대학 이름은 필수 입력값입니다.");

return UserUniversity.builder()
.userId(userId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.moneymong.domain.user.repository;

import com.moneymong.domain.user.entity.AppleUser;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AppleUserRepository extends JpaRepository<AppleUser, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.moneymong.domain.agency.service.AgencyUserService;
import com.moneymong.domain.user.api.request.LoginRequest;
import com.moneymong.domain.user.api.request.UserDeleteRequest;
import com.moneymong.global.security.oauth.dto.AuthUserInfo;
import com.moneymong.domain.user.api.response.LoginSuccessResponse;
import com.moneymong.global.security.oauth.dto.OAuthUserDataResponse;
Expand Down Expand Up @@ -43,4 +44,10 @@ public void delete(Long userId) {
userUniversityService.delete(userId);
agencyUserService.deleteAll(userId);
}

@Transactional
public void revoke(UserDeleteRequest deleteRequest, Long userId) {
oAuthService.revoke(deleteRequest);
delete(userId);
}
}
36 changes: 28 additions & 8 deletions src/main/java/com/moneymong/domain/user/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.moneymong.domain.user.service;

import com.moneymong.domain.user.api.response.UserProfileResponse;
import com.moneymong.domain.user.entity.AppleUser;
import com.moneymong.domain.user.entity.User;
import com.moneymong.domain.user.entity.UserUniversity;
import com.moneymong.domain.user.repository.AppleUserRepository;
import com.moneymong.domain.user.repository.UserRepository;
import com.moneymong.domain.user.repository.UserUniversityRepository;
import com.moneymong.global.exception.custom.NotFoundException;
Expand All @@ -25,19 +27,13 @@ public class UserService {
private final UserRepository userRepository;
private final UserUniversityRepository userUniversityRepository;
private final RefreshTokenRepository refreshTokenRepository;
private final AppleUserRepository appleUserRepository;

@Transactional
public AuthUserInfo getOrRegister(OAuthUserInfo oauthUserInfo) {
User user = userRepository
.findByUserIdByProviderAndOauthId(oauthUserInfo.getProvider(), oauthUserInfo.getOauthId())
.orElseGet(() -> save(
User.of(UUID.randomUUID().toString(),
oauthUserInfo.getEmail(),
oauthUserInfo.getNickname(),
oauthUserInfo.getProvider(),
oauthUserInfo.getOauthId()
)
));
.orElseGet(() -> registerUser(oauthUserInfo));

return AuthUserInfo.from(user.getId(), user.getNickname(), DEFAULT_ROLE);
}
Expand All @@ -47,6 +43,30 @@ public User save(User unsavedUser) {
return userRepository.save(unsavedUser);
}

@Transactional
public User registerUser(OAuthUserInfo oauthUserInfo) {
User newUser = User.of(
UUID.randomUUID().toString(),
oauthUserInfo.getEmail(),
oauthUserInfo.getNickname(),
oauthUserInfo.getProvider(),
oauthUserInfo.getOauthId()
);
newUser = save(newUser);

if (oauthUserInfo.getAppleRefreshToken() != null) {
appleUserRepository.save(
AppleUser.of(
newUser.getId(),
oauthUserInfo.getAppleRefreshToken()
)
);
}

return newUser;
}


@Transactional(readOnly = true)
public UserProfileResponse getUserProfile(Long userId) {
User user = userRepository.findById(userId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.moneymong.global.security.oauth.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class AppleUserData {
@JsonProperty("refresh_token")
private String refreshToken;

@JsonProperty("id_token")
private String idToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
@AllArgsConstructor
public class OAuthUserDataRequest {
private String accessToken;
private String code; // Apple Authorization Code
private String name;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public class OAuthUserDataResponse {
private String oauthId;
private String email;
private String nickname;
private String appleRefreshToken;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.moneymong.global.security.oauth.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;

@Getter
@Builder
Expand All @@ -14,13 +11,15 @@ public class OAuthUserInfo {
private String oauthId;
private String nickname;
private String email;
private String appleRefreshToken;

public static OAuthUserInfo from(OAuthUserDataResponse oAuthUserDataResponse) {
return OAuthUserInfo.builder()
.provider(oAuthUserDataResponse.getProvider())
.oauthId(oAuthUserDataResponse.getOauthId())
.nickname(oAuthUserDataResponse.getNickname())
.email(oAuthUserDataResponse.getEmail())
.appleRefreshToken(oAuthUserDataResponse.getAppleRefreshToken())
.build();
}
}
Loading

0 comments on commit 4af195a

Please sign in to comment.