Skip to content

Commit

Permalink
Merge pull request #62 from Modagbul/fix/auth
Browse files Browse the repository at this point in the history
Fix/auth
  • Loading branch information
minsu20 authored Nov 6, 2023
2 parents 0279a8e + 284f7ac commit af8e840
Show file tree
Hide file tree
Showing 24 changed files with 292 additions and 42 deletions.
2 changes: 1 addition & 1 deletion src/docs/asciidoc/Mypage-API.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ operation::mypage-controller-test/sign_out[snippets='http-request,http-response,

[[Mypage-회원탈퇴]]
== Mypage 회원탈퇴
operation::mypage-controller-test/withdraw[snippets='http-request,request-fields,http-response,response-fields']
operation::mypage-controller-test/withdraw[snippets='path-parameters,http-request,request-fields,http-response,response-fields']

[[Mypage-전체-조회]]
== Mypage 전체 조회
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public SignInResponse signIn(SignInRequest signInRequest, String providerInfo) {
}

private Member getUserDataFromPlatform(String accessToken, String providerInfo) {
SignInProvider signInProvider = signInProviders.get(providerInfo);
SignInProvider signInProvider = signInProviders.get(providerInfo+"SignIn");
if (signInProvider == null) {
throw new IllegalArgumentException("Unknown provider: " + providerInfo);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.moing.backend.domain.auth.application.service;

import java.io.IOException;

public interface WithdrawProvider {
void withdraw(String token) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

@Service("apple")
@Service("appleSignIn")
@AllArgsConstructor
public class AppleSignInUserCase implements SignInProvider {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.moing.backend.domain.auth.application.service.apple;

import com.moing.backend.domain.auth.application.service.apple.feign.AppleFeignClient;
import com.moing.backend.domain.auth.application.service.apple.feign.response.Keys;
import com.moing.backend.domain.auth.application.service.apple.utils.AppleClient;
import com.moing.backend.domain.auth.application.service.apple.utils.Keys;
import com.moing.backend.domain.auth.exception.TokenInvalidException;
import com.moing.backend.global.exception.InternalServerErrorException;
import io.jsonwebtoken.*;
Expand All @@ -21,10 +21,10 @@
@RequiredArgsConstructor
public class AppleTokenUserCase {

@Value("${app-id.apple}")
@Value("${oauth2.apple.clientId}")
private String appId;

private final AppleFeignClient appleFeignClient;
private final AppleClient appleClient;

public Jws<Claims> sigVerificationAndGetJws(String unverifiedToken) {

Expand All @@ -34,7 +34,7 @@ public Jws<Claims> sigVerificationAndGetJws(String unverifiedToken) {
"https://appleid.apple.com",
appId);

Keys keys = appleFeignClient.getKeys();
Keys keys = appleClient.getKeys();
Keys.PubKey pubKey = keys.getKeys().stream()
.filter((key) -> key.getKid().equals(kid))
.findAny()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.moing.backend.domain.auth.application.service.apple;

import com.moing.backend.domain.auth.application.service.WithdrawProvider;
import com.moing.backend.domain.auth.application.service.apple.utils.AppleClient;
import com.moing.backend.domain.auth.application.service.apple.utils.AppleToken;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.security.Key;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service("appleWithdraw")
@RequiredArgsConstructor
public class AppleWithdrawUserCase implements WithdrawProvider {

@Value("${oauth2.apple.keyId}")
private String keyId;

@Value("${oauth2.apple.teamId}")
private String teamId;

@Value("${oauth2.apple.clientId}")
private String clientId;

@Value("${oauth2.apple.keyPath}")
private String keyPath;

private final AppleClient appleClient;

public void withdraw(String token) throws IOException {
AppleToken.Response response = generateAuthToken(token);

if (response.getAccess_token() != null) {
appleClient.revoke(AppleToken.RevokeRequest.of(
clientId,
createClientSecret(),
response.getAccess_token()
)
);
}
}

public AppleToken.Response generateAuthToken(String authorizationCode) throws IOException {

return appleClient.getToken(AppleToken.Request.of(
authorizationCode,
clientId,
createClientSecret(),
"authorization_code"
));
}

public String createClientSecret() throws IOException {
Date expirationDate = Date.from(LocalDateTime.now().plusDays(30).atZone(ZoneId.systemDefault()).toInstant());
Map<String, Object> jwtHeader = new HashMap<>();
jwtHeader.put("kid", keyId);
jwtHeader.put("alg", "ES256");

return Jwts.builder()
.setHeaderParams(jwtHeader)
.setIssuer(teamId)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(expirationDate)
.setAudience("https://appleid.apple.com")
.setSubject(clientId)
.signWith(getPrivateKey(), SignatureAlgorithm.ES256)
.compact();
}

private Key getPrivateKey() throws IOException {
ClassPathResource resource = new ClassPathResource(keyPath);
String privateKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));

Reader pemReader = new StringReader(privateKey);
PEMParser pemParser = new PEMParser(pemReader);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject();
return converter.getPrivateKey(object);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.moing.backend.domain.auth.application.service.apple.utils;

import com.moing.backend.global.util.FeignClientConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@FeignClient(name = "appleClient", url = "https://appleid.apple.com/auth", configuration = FeignClientConfig.class)
public interface AppleClient {
@GetMapping(value = "/keys")
Keys getKeys();
@PostMapping(value = "/token", consumes = "application/x-www-form-urlencoded")
AppleToken.Response getToken(AppleToken.Request request);

@PostMapping(value = "/revoke", consumes = "application/x-www-form-urlencoded")
void revoke(AppleToken.RevokeRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.moing.backend.domain.auth.application.service.apple.utils;

import lombok.Getter;

public class AppleToken {

public static class Request {
private String code;
private String client_id;
private String client_secret;
private String grant_type;

public static Request of(String code, String clientId, String clientSecret, String grantType) {
Request request = new Request();
request.code = code;
request.client_id = clientId;
request.client_secret = clientSecret;
request.grant_type = grantType;
return request;
}
}

@Getter
public static class Response {
private String access_token;
private String expires_in;
private String id_token;
private String refresh_token;
private String token_type;
private String error;
}

@Getter
public static class RevokeRequest {
private String client_id;
private String client_secret;
private String token;

public static RevokeRequest of(String clientId, String clientSecret, String token) {
RevokeRequest request = new RevokeRequest();
request.client_id = clientId;
request.client_secret = clientSecret;
request.token = token;
return request;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moing.backend.domain.auth.application.service.apple.feign.response;
package com.moing.backend.domain.auth.application.service.apple.utils;

import lombok.Data;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@

import com.moing.backend.domain.auth.application.dto.response.GoogleUserResponse;
import com.moing.backend.domain.auth.application.service.SignInProvider;
import com.moing.backend.domain.auth.exception.AppIdInvalidException;
import com.moing.backend.domain.auth.exception.TokenInvalidException;
import com.moing.backend.domain.member.application.mapper.MemberMapper;
import com.moing.backend.domain.member.domain.entity.Member;
import com.moing.backend.global.exception.InternalServerErrorException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Arrays;

@Service("google")
@Service("googleSignIn")
@RequiredArgsConstructor
public class GoogleSignInUserCase implements SignInProvider {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@RequiredArgsConstructor
public class GoogleTokenUserCase {

@Value("${app-id.google}")
@Value("${oauth2.google.appId}")
private String appId;

public void verifyAccessToken(String aud) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.moing.backend.domain.auth.application.service.google;

import com.moing.backend.domain.auth.application.service.WithdrawProvider;
import com.moing.backend.domain.auth.application.service.google.utils.GoogleClient;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service("googleWithdraw")
@RequiredArgsConstructor
public class GoogleWithdrawUserCase implements WithdrawProvider {

private final GoogleClient googleClient;

public void withdraw(String token) throws IOException {
googleClient.revoke(token);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moing.backend.domain.auth.application.service.google.utils;

import com.moing.backend.global.util.FeignClientConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "googleClient", url = "https://oauth2.googleapis.com", configuration = FeignClientConfig.class)
public interface GoogleClient {
@PostMapping(value = "/revoke", consumes = "application/x-www-form-urlencoded")
void revoke(@RequestParam("token") String token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service("kakao")
@Service("kakaoSignIn")
@RequiredArgsConstructor
public class KakaoSignInUserCase implements SignInProvider {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@RequiredArgsConstructor
public class KakaoTokenUserCase {

@Value("${app-id.kakao}")
@Value("${oauth2.kakao.appId}")
private String appId;

private final WebClient webClient;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.moing.backend.domain.auth.application.service.kakao;

import com.moing.backend.domain.auth.application.service.WithdrawProvider;
import com.moing.backend.domain.auth.application.service.kakao.utils.KakaoClient;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service("kakaoWithdraw")
@RequiredArgsConstructor
public class KakaoWithdrawUserCase implements WithdrawProvider {

private final KakaoClient kakaoClient;

public void withdraw(String token) throws IOException {

kakaoClient.unlinkUser("Bearer " + token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moing.backend.domain.auth.application.service.kakao.utils;

import com.moing.backend.global.util.FeignClientConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;

@FeignClient(name = "kakaoClient", url = "https://kapi.kakao.com", configuration = FeignClientConfig.class)
public interface KakaoClient {
@PostMapping("/v1/user/unlink")
KakaoUnlinkResponse unlinkUser(@RequestHeader("Authorization") String accessToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.moing.backend.domain.auth.application.service.kakao.utils;

import lombok.Getter;

@Getter
public class KakaoUnlinkResponse {
private String id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ public class WithdrawRequest {
@NotBlank(message = "reason 을 입력해주세요.")
@Size(min = 1, max = 500, message="reason 은 최소 1개, 최대 500개의 문자만 입력 가능합니다.")
private String reason;

@NotBlank(message = "socialToken 을 입력해주세요.")
private String socialToken;
}
Loading

0 comments on commit af8e840

Please sign in to comment.