Search
🚑

AGU 유저지만 반납이 하고싶어.. OAuth2 Multi-Provider 로그인 / 메일 인증을 통한 임시 로그인

글감
Spring
Java
작성자
작성 일자
2025/04/08 04:32
상태
완료
공개여부
공개
Date
2025/04/08
생성자
작업자
꽤 잦은 요청이 오는 눈물의 수동 반납작업 처리.. 왜 일어나게 된걸까?

문제상황

42가 AGU라는 휴학 롤을 추가하였고, 해당 기능을 활성화하는 순간 해제할때까지 슬랙 비활성화 및 관련 42 oauth에는 모두 로그인에 실패하게 된다.
이로 인해 AGU 신청 → 42OAuth 로그인 불가능 → cabi 접속 불가 → 제발.. 강제반납좀

해결 방식

1.
계정 연동 - 마이페이지에서 계정 연동, AGU 이후에는 42 OAuth2 로그인이 아닌 다른 SNS 로그인으로 접속한다
2.
임시 로그인 - 짧은 유효기간을 가진 VerificationCode를 담아 링크를 만들어 이메일로 발송, 유저가 링크 클릭 시 BE에게 요청을 보내 대조를한 후 성공 시 토큰을 발급 → AGU 유저를 위한 페이지로 redirect

해결 과정

하지만 대다수의 유저는 예? 저는 계정 연동 안하고 이미 AGU 눌렀어요 어떡해요? 일것..이지만 상관 없다. 휴학처리했는데 사물함을 반납하려는 의지가 1%라도 있다? 그것만으로도 당신은 1000점짜리 유저.
잇츠오케이. 당신? 이미 구제됐어. 문제를 해결하기 위해 시나리오를 다음과 같이 구축했다.
클라이언트가 intraName을 보내 요청 → BE는 실제 있는 유저인지 쿼리, 대여중인 상태라면 유저의 이메일로 임시 로그인 링크 발송, name:코드 redis에 저장
유저가 이메일의 링크 클릭 → BE는 redis에서 코드를 가져와 대조 → /agu 페이지로 리다이렉트
구현만 하면 끝이다!

42 OAuth2 AccessToken 준비하기

해당 유저가 AGU인지 판별하기 위해 42 api를 호출해서 정보를 받아오고, 이를 기반으로 Role을 결정해야한다. 이를 위해 client-credentials 방식으로 미리 42 OAuth AccessToken을 준비해놓도록 하자.
/** * 서버 내에서 외부 API를 사용하기 위한 토큰을 정적으로 관리하는 클래스입니다. */ @Log4j2 @Component @RequiredArgsConstructor public class ApplicationTokenManager { private static final int MAX_RETRY = 3; private static final String CLIENT_REGISTRATION_ID = "ft-client-credentials"; private static String FT_ACCESS_TOKEN; private final OAuth2AuthorizedClientManager authorizedClientManager; /** * 서버가 시작될 때, 42 OAuth 액세스 토큰을 발급합니다. */ @PostConstruct private void init() { this.refreshFtAccessToken(); } public String getFtAccessToken() { return FT_ACCESS_TOKEN; } /** * 42 OAuth 액세스 토큰을 새로 발급합니다. 최대 3번까지 재시도합니다. */ public void refreshFtAccessToken() { int tryCount = 0; while (++tryCount <= MAX_RETRY) { try { OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(CLIENT_REGISTRATION_ID) .principal("client-credentials") .build(); OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); if (authorizedClient == null || authorizedClient.getAccessToken() == null) { throw ExceptionStatus.OAUTH_BAD_GATEWAY.asServiceException(); } FT_ACCESS_TOKEN = authorizedClient.getAccessToken().getTokenValue(); log.info("토큰 발급 완료"); break; } catch (Exception e) { log.error("42 OAuth 액세스 토큰을 발급하는 데에 실패했습니다. 현재 시도 횟수 : {}, {}", tryCount, e.getMessage()); } } if (tryCount == MAX_RETRY) { log.error("42 OAuth 액세스 토큰을 발급하는 데에 실패했습니다. OAuth 서버가 응답하지 않습니다."); throw ExceptionStatus.OAUTH_BAD_GATEWAY.asServiceException(); } } }
Java
복사
client-credentials 형식으로 유저의 로그인 없이 AccessToken을 서버에 준비해놓고
public FtOauthProfile getProfileByIntraName(String intraName) throws JsonProcessingException { log.info("Called getProfileByIntraName {}", intraName); try { return callFtApi(applicationTokenManager.getFtAccessToken(), intraName); } catch (WebClientResponseException.Unauthorized e) { log.warn("FT Access 토큰 만료, 재발급 시도중.."); applicationTokenManager.refreshFtAccessToken(); return callFtApi(applicationTokenManager.getFtAccessToken(), intraName); } }
Java
복사
42 Api를 요청해 해당 Role이 담겨있는 프로필을 반환하고 만약 accessToken이 만료되었을 경우 retry 로직을 호출한다.
실제 유저가 있는지, AGU 유저인지, 대여중인지, 이미 코드를 발송한 상태인지에 대해 검증을 수행하고 모두 통과했다면 인증링크를 만들고 담아 유저에게 이메일을 발송한다
Cabi 에는 이미 알람 발송 시스템이 구현되어있기 때문에, 이메일 발송 기능도 거기에 스리슬쩍 끼워넣어 보자.
JavaMailSender를 사용해 메일을 발송하는 AlarmEventListener에 더부살이를 좀..,
이후에 유저가 임시 로그인 링크를 클릭하게 되면 BE의 api 호출을 타게되고,
해당 로직을 통해 redis로 관리하고 있는 임시 코드를 가져와 비교를 수행한 뒤 올바른 코드라면 AGU 토큰을 발급해 쿠키에 담아 FE/agu 페이지로 리다이렉트를 수행한다.
구현 완료 ^0^~
해당 기능을 통해 AGU 유저들이 편하게 사물함을 반납하고, 까비 팀원들은 수동반납으로 고통받지 않길..