𝝅번째 알파카의 개발 낙서장

screen

[OAuth2.0] ScribeJAVA로 OAuth2.0 인증서버 구축하기 - 7. GitHub OAuth 서비스 신청 및 모듈 구현하기

posts

JAVA

시리즈 톺아보기

OAuth2.0 인증서버 구축기

OAuth2.0 인증서버 구축기
count

개요 🔗

마지막 플랫폼으로, GitHub에 OAuth 서비스를 신청하고 인증 모듈을 구현한다.

GitHub OAuth 서비스 신청하기 🔗

API 정보를 얻기 위해 GitHub OAuth 서비스를 신청하자.

1. OAuth Apps 생성하기 🔗

로그인 후 GitHub Developer Settings에 접속하자.

상단 프로필 메뉴에서 [Setting - Developer Settings - OAuth Apps]를 클릭하여 접속할 수도 있다.

[New OAuth App]을 클릭하여 새로운 애플리케이션을 생성하자.


필수사항을 입력하면 된다. GitHub는 Callback URL를 하나만 입력할 수 있는 것 같다.

2. Client Secret 생성하기 🔗

생성한 애플리케이션을 클릭한다. [Generate a new client secret]을 클릭하여 새로운 Client Secret을 발급받는다. 암호 확인 과정이 필요하다.

생성 직후 키를 보여주며, 창을 닫게 되면 해당 키는 다시 확인할 수 없으므로 적당한 곳에 임시로 기록해두던지 하자.

잊어버릴 경우 다시 발급받아야한다.

3. API 키 확인 🔗

메인 화면인 [General]에서 확인할 수 있다.

image

Client ID는 상시 확인 가능하고, Client Secret은 발급 직후에만 일시적으로 확인 가능하다는 점을 주의하자

GitHub OAuth는 이게 끝이다. 별도의 정보 동의 과정도 요구하지 않는다. 그도 그럴 것이, GitHub의 OAuth 키는 프로필 정보만 불러올 수 있기 때문.

GitHub 인증 모듈 구현하기 🔗

필요한 모든 준비가 갖춰졌으니, GitHub 인증 모듈을 구현해보자. 이전에 구현한 AuthModule을 상속받아 구현할 것이다.

JAVA

0public class GithubAuthModule extends AuthModule
1{
2 // GitHub 인증 모듈
3}

객체의 기본 형식은 위와 같다.

메서드 메서드 타입 내용 구현 필요 여부
getAuthorizationUrl 추상 인증 URL 반환 메서드 Y
getAccessToken 접근 토큰 반환 메서드 Y
getRefreshAccessToken 접근 토큰 갱신 및 반환 메서드
getUserInfo 사용자 정보 응답 반환 메서드 Y
getRefreshTokenEndpoint 접근 토큰 재발급 요청 URL 반환 메서드
getApiKeyBean API 키 객체 반환 메서드
getUserInfoEndPoint 사용자 정보 요청 URL 반환 메서드
getUserInfoBean 추상 유저 정보 객체 반환 메서드 Y
deleteInfo 추상 연동 해제 결과 반환 메서드 Y
getUpdateAuthorizationUrl 추상 정보 제공 동의 갱신 URL 반환 메서드 Y
getAccessTokenEndpoint 추상 접근 토큰 요청 URL 반환 메서드 Y
getAuthorizationBaseUrl 추상 인증 API 요청 URL 반환 메서드 Y

GutHub 모듈이 구현해야하는 대상은 위와 같다. 이전 플랫폼과 다르게 getAccessToken, getUserInfo의 오버라이딩이 필요하다.

properties 파일 생성하기 🔗

WEB-INF 아래 github.properties 파일을 생성한다. 기 생성된 sample.properties를 복사해서 사용해도 된다.

PROPERTIES

0api=API_KEY
1secret=SECRET_KEY
2callback=CALLBACK_URL

기본적인 형식은 위와 같으며, 각 항목에 해당하는 값을 입력하면 된다.

인증 모듈 기본 메서드 및 변수 할당하기 🔗

인증 모듈이 정상적으로 동작하기 위해선 기본적으로 지정해줘야할 메서드와 변수들이 존재한다. API 정보 설정, 인스턴스 반환같은 것들이다.

JAVA

0private static final String MODULE_NAME = "github";
1
2private static final String API_KEY;
3private static final String SECRET_KEY;
4private static final String CALLBACK_URL;
5
6static
7{
8 ApiKeyBean apiKeyBean = getApiKeyBean(MODULE_NAME);
9
10 API_KEY = apiKeyBean.getApi();
11 SECRET_KEY = apiKeyBean.getSecret();
12 CALLBACK_URL = apiKeyBean.getCallback();
13}
14
15private static final ServiceBuilderOAuth20 SERVICE_BUILDER = new ServiceBuilder(API_KEY).apiSecret(SECRET_KEY).callback(CALLBACK_URL);
16
17private static final GithubAuthModule INSTANCE = new GithubAuthModule(SERVICE_BUILDER);
18
19private GithubAuthModule(ServiceBuilderOAuth20 serviceBuilder)
20{
21 super(serviceBuilder);
22}
23
24public static GithubAuthModule getInstance()
25{
26 return INSTANCE;
27}
구분 형식 내용
MODULE_NAME String 모듈 이름
API_KEY String API키
SECRET_KEY String Secret키
CALLBACK_URL String 콜백 URL
SERVICE_BUILDER ServiceBuilderOAuth20 OAuth2.0 서비스 빌더
INSTANCE GithubAuthModule 인스턴스

정의된 변수는 전부 static final로 선언되어 있어서, 인스턴스 생성 시 한 번만 선언되며 재할당이 불가능하도록 관리한다.

static{ } 구문을 통해 인스턴스 생성 시 API 정보를 할당하도록 구성했다.

API 할당 시 getApiKeyBean() 메서드를 통해 제공된 이름을 갖는 properties를 분석하여 ApiKeyBean 객체를 반환받아 사용한다.

API URL 할당하기 🔗

각 API 별 요청 URL을 반환하는 메서드를 구현하자.

JAVA

0@Override
1public String getAccessTokenEndpoint()
2{
3 return "https://github.com/login/oauth/access_token";
4}
5
6@Override
7protected String getAuthorizationBaseUrl()
8{
9 return "https://github.com/login/oauth/authorize";
10}
11
12@Override
13protected String getUserInfoEndPoint()
14{
15 return "https://api.github.com/users/RWB0104";
16}
  • getAccessTokenEndpoint() - 토큰과 관련된 API는 해당 메소드가 반환하는 URL을 토대로 사용한다.
  • getAuthorizationBaseUrl() - 인증과 관련된 API는 해당 메소드가 반환하는 URL을 토대로 사용한다.
  • getUserInfoEndPoint() - 사용자 정보와 관련된 API는 해당 메소드가 반환하는 URL을 토대로 사용한다.

OAuth2.0 서비스를 수행함에 있어서 필요한 URL은 위와 같다. 이 중 getAccessTokenEndpoint()getAuthorizationBaseUrl()는 scribeJAVA 라이브러리의 객체인 DefaultApi20의 추상 메서드고 나머지 하나가 AuthModule의 추상 메서드다.

DefaultApi20는 사용자 계정 API에 관련된 메서드를 별도로 제공하지 않는다. 하지만 AuthModule에서 사용자 정보 확인 공통 메서드를 사용할 때 사용자 계정 API가 반드시 필요하므로 AuthModule의 추상 메서드로 관리한다.

인증 URL 반환 메서드 🔗

GitHub 플랫폼 로그인 URL을 반환하는 기능을 구현한다.

우선 API를 살펴보자.


  • 요청

TXT

0GET https://github.com/login/oauth/authorize?response_type=code&client_id={:client_id}&redirect_uri={:redirect_uri}&state={:state}
parameter type data required description
{:response_type} path String Y 응답 타입. code로 고정
{:client_id} path String Y API키
{:redirect_uri} path String Y Callback URL
{:state} path String Y 고유 상태값

  • 응답

GitHub 플랫폼 로그인 페이지


GitHub 플랫폼 로그인 API는 위와 같다. 메서드가 요청의 URL을 반환하도록 설계하면 된다.

문자열 연산으로 URL을 직접 설계할 수도 있지만, service.getAuthorizationUrl() 메서드를 통해 URL을 간편하게 생성할 수 있다.


이미 AuthModule에 공통 메서드로 선언된 게 있으므로, 따로 구현하지 않아도 된다.

접근 토큰 반환 메서드 🔗

로그인 결과로 Code를 전달받으므로 Access Token으로 교환하는 기능을 구현한다.

GitHub는 Accept 헤더로 JSON MIME를 지정해야하는데, 아쉽게도 scribeJAVA에는 임의 헤더를 넣어 인증 URL을 생성하는 API는 존재하지 않는다.

Access Token도 없으므로 HttpURLConnection으로 직접 구현해야한다.


  • 요청

TXT

0POST https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={:client_id}&client_secret={:client_secret}&redirect_uri={:redirect_uri}&code={:code}
1Accept: application/json
parameter type data required description
{:grant_type} path String Y 인증 타입. authorization_code로 고정
{:client_id} path String Y API키
{:client_secret} path String Y Secret키
{:redirect_uri} path String Y Callback URL
{:code} path String Y 인가 코드

  • 응답

JSON

0{
1 "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
2 "scope": "repo,gist",
3 "token_type": "bearer"
4}
parameter data description
access_token String 인증 토큰
token_type String 토큰 타입
scope String 접근 권한

AccessTokenRequestParams 객체로 요청을 생성하여 service.getAccessToken으로 응답을 받는다.

특이하게 응답 헤더를 반드시 지정해야하므로, 별도로 오버라이딩해서 사용한다.

접근 토큰 갱신 및 반환 메서드 🔗

위 응답을 보면 알 수 있듯이, GitHub는 Refresh Token이 따로 존재하지 않는다. Access Token의 만료시간도 없다. GitHub는 그냥 Access Token 하나만 다루게 된다.

Refresh Token이 없으므로 기능 자체가 쓸모가 없다. 따라서 Github에선 건들지 않는다.

사용자 정보 응답 반환 메서드 🔗

Access Token으로 유저 정보를 호출하는 기능을 구현한다. 발급받은 Access Token을 실제로 유의미하게 쓰는 부분이다.

GitHub API는 아래와 같다.


  • 요청

TXT

0GET/POST https://api.github.com/user
1Authorization: token {:access_token}
parameter type data required description
{:access_token} header String Y 접근 토큰

  • 응답

JSON

0{
1 "login": "octocat",
2 "id": 1,
3 "node_id": "MDQ6VXNlcjE=",
4 "avatar_url": "https://github.com/images/error/octocat_happy.gif",
5 "gravatar_id": "",
6 "url": "https://api.github.com/users/octocat",
7 "html_url": "https://github.com/octocat",
8 "followers_url": "https://api.github.com/users/octocat/followers",
9 "following_url": "https://api.github.com/users/octocat/following{/other_user}",
10 "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
11 "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
12 "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
13 "organizations_url": "https://api.github.com/users/octocat/orgs",
14 "repos_url": "https://api.github.com/users/octocat/repos",
15 "events_url": "https://api.github.com/users/octocat/events{/privacy}",
16 "received_events_url": "https://api.github.com/users/octocat/received_events",
17 "type": "User",
18 "site_admin": false,
19 "name": "monalisa octocat",
20 "company": "GitHub",
21 "blog": "https://github.com/blog",
22 "location": "San Francisco",
23 "email": "octocat@github.com",
24 "hireable": false,
25 "bio": "There once was...",
26 "twitter_username": "monatheoctocat",
27 "public_repos": 2,
28 "public_gists": 1,
29 "followers": 20,
30 "following": 0,
31 "created_at": "2008-01-14T04:33:35Z",
32 "updated_at": "2008-01-14T04:33:35Z",
33 "private_gists": 81,
34 "total_private_repos": 100,
35 "owned_private_repos": 100,
36 "disk_usage": 10000,
37 "collaborators": 8,
38 "two_factor_authentication": true,
39 "plan": {
40 "name": "Medium",
41 "space": 400,
42 "private_repos": 20,
43 "collaborators": 0
44 }
45}

응답 명세는 GitHub에서 명확하게 제공하지 않는다. 확실한건 email, login, avatar_url를 쓰면 될 것 같다.

유저 정보 객체 반환 메서드 🔗

GitHub의 유저 정보 호출 API 응답 형식에 맞게끔 응답을 파싱하여 UserInfoBean로 반환하는 메서드를 구현한다.

이 프로젝트에선 이름, 이메일, 프로필사진 URL만을 사용하므로, 응답에서 해당 값을 빼내어 객체에 담는다.


  • 코드

JAVA

0@Override
1public UserInfoBean getUserInfoBean(String body) throws JsonProcessingException
2{
3 ObjectMapper mapper = new ObjectMapper();
4
5 JsonNode node = mapper.readTree(body);
6
7 String email = node.get("email") == null ? "미동의" : node.get("email").textValue();
8 String name = node.get("name") == null ? "미동의" : node.get("name").textValue();
9 String picture = node.get("avatar_url") == null ? "/oauth2/assets/images/logo.png" : node.get("avatar_url").textValue();
10
11 return new UserInfoBean(email, name, picture, MODULE_NAME);
12}

응답 형식에 맞추어 필요한 값을 추출한다. 만약, 사용자가 정보 제공에 동의하지 않았을 경우 대상 객체가 null을 반환한다. 데이터의 누락 시 오류를 방지하기 위해 데이터에 대한 null 처리를 반드시 해야한다.

연동 해제 결과 반환 메서드 🔗

카카오 아이디로 처음 로그인을 하면 정보 제공 동의를 수행하는데, 나중에 다시 로그인을 하면 이러한 동의 과정이 생략된다. 즉, 플랫폼에서 첫 로그인 시 정보 제공 동의를 받아 어딘가로부터 저장한다는 뜻이다. 만약 사용자가 서비스로부터 회원 탈퇴를 수행한다면 GitHub와의 연동을 해제하여 정보를 완전히 삭제할 필요가 있다.

GitHub API는 아래와 같다.


  • 요청

TXT

0DELETE https://api.github.com/applications/{:client_id}/grant
1Authorization: Basic {:auth}
2Accept: application/vnd.github.v3+json
3Content-Type: application/x-www-form-urlencoded
parameter type data required description
{:client_id} path String Y API 키
{:auth} header String Y API 키와 Secret 키의 Basic 인증

ID:PW 기반의 Basic 헤더
Basic 헤더는 ID:PW 기반의 인증 방식이다. ID와 PW를 [ID:PW]와 같이 :로 조인한 하나의 문자열로 만든다. 해당 텍스트를 헤더에 사용한다.


  • 응답

응답은 204로, 아무도 오지 않는다.


  • 코드

JAVA

0@Override
1public boolean deleteInfo(String access) throws IOException, ExecutionException, InterruptedException
2{
3 HashMap<String, String> params = new HashMap<>();
4 params.put("access_token", access);
5
6 ObjectMapper mapper = new ObjectMapper();
7
8 byte[] paramBytes = mapper.writeValueAsString(params).getBytes(StandardCharsets.UTF_8);
9
10 URL url = new URL(Util.builder("https://api.github.com/applications/", API_KEY, "/grant"));
11
12 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
13 connection.setRequestMethod("DELETE");
14 connection.addRequestProperty("Authorization", Util.builder("Basic ", Base64.getEncoder().encodeToString(Util.builder(API_KEY, ":", SECRET_KEY).getBytes())));
15 connection.addRequestProperty("Accept", "application/vnd.github.v3+json");
16 connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
17 connection.setDoOutput(true);
18 connection.getOutputStream().write(paramBytes);
19
20 int status = connection.getResponseCode();
21
22 connection.disconnect();
23
24 return status == 204;
25}

구현은 간단하다. OAuthRequest 객체를 활용하면 요청을 쉽게 생성할 수 있다. 응답 자체는 중요하지 않다. 이번엔 응답이 204이므로, 응답 상태값이 204인지 비교하여 결과를 반환한다.

정보 제공 동의 갱신 URL 반환 메서드 🔗

GitHub는 별도의 동의가 이루어지지 않으므로 무시한다.

전체 코드 🔗

JAVA

0package oauth.account.module;
1
2import com.fasterxml.jackson.core.JsonProcessingException;
3import com.fasterxml.jackson.databind.JsonNode;
4import com.fasterxml.jackson.databind.ObjectMapper;
5import com.github.scribejava.core.builder.ServiceBuilder;
6import com.github.scribejava.core.builder.ServiceBuilderOAuth20;
7import com.github.scribejava.core.model.OAuth2AccessToken;
8import com.github.scribejava.core.model.OAuthRequest;
9import com.github.scribejava.core.model.Response;
10import com.github.scribejava.core.model.Verb;
11import global.module.Util;
12import oauth.account.bean.ApiKeyBean;
13import oauth.account.bean.UserInfoBean;
14
15import java.io.BufferedReader;
16import java.io.IOException;
17import java.io.InputStreamReader;
18import java.net.HttpURLConnection;
19import java.net.URL;
20import java.net.URLEncoder;
21import java.nio.charset.StandardCharsets;
22import java.util.Base64;
23import java.util.HashMap;
24import java.util.Map;
25import java.util.concurrent.ExecutionException;
26
27/**
28 * GitHub 인증 모듈 클래스
29 *
30 * @author RWB
31 * @since 2021.10.05 Tue 00:22:10
32 */
33public class GithubAuthModule extends AuthModule
34{
35 private static final String MODULE_NAME = "github";
36
37 private static final String API_KEY;
38 private static final String SECRET_KEY;
39 private static final String CALLBACK_URL;
40
41 static
42 {
43 ApiKeyBean apiKeyBean = getApiKeyBean(MODULE_NAME);
44
45 API_KEY = apiKeyBean.getApi();
46 SECRET_KEY = apiKeyBean.getSecret();
47 CALLBACK_URL = apiKeyBean.getCallback();
48 }
49
50 private static final ServiceBuilderOAuth20 SERVICE_BUILDER = new ServiceBuilder(API_KEY).apiSecret(SECRET_KEY).callback(CALLBACK_URL);
51
52 private static final GithubAuthModule INSTANCE = new GithubAuthModule(SERVICE_BUILDER);
53
54 /**
55 * 생성자 메서드
56 *
57 * @param serviceBuilder: [ServiceBuilderOAuth20] API 서비스 빌더
58 */
59 private GithubAuthModule(ServiceBuilderOAuth20 serviceBuilder)
60 {
61 super(serviceBuilder);
62 }
63
64 /**
65 * 인스턴스 반환 메서드
66 *
67 * @return [GithubAuthModule] 인스턴스
68 */
69 public static GithubAuthModule getInstance()
70 {
71 return INSTANCE;
72 }
73
74 /**
75 * 접근 토큰 반환 메서드
76 *
77 * @param code: [String] 인증 코드
78 *
79 * @return [OAuth2AccessToken] 접근 토큰
80 *
81 * @throws IOException 데이터 입출력 예외
82 */
83 @Override
84 public OAuth2AccessToken getAccessToken(String code) throws IOException
85 {
86 HashMap<String, String> params = new HashMap<>();
87 params.put("client_id", API_KEY);
88 params.put("client_secret", SECRET_KEY);
89 params.put("redirect_uri", CALLBACK_URL);
90 params.put("code", code);
91
92 StringBuilder builder = new StringBuilder();
93
94 for (Map.Entry<String, String> param : params.entrySet())
95 {
96 String pre = builder.length() == 0 ? "" : "&";
97
98 builder.append(pre).append(URLEncoder.encode(param.getKey(), StandardCharsets.UTF_8)).append("=").append(URLEncoder.encode(param.getValue(), StandardCharsets.UTF_8));
99 }
100
101 byte[] paramBytes = builder.toString().getBytes(StandardCharsets.UTF_8);
102
103 URL url = new URL(getAccessTokenEndpoint());
104
105 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
106 connection.setRequestMethod("POST");
107 connection.setRequestProperty("Accept", "application/json");
108 connection.setDoOutput(true);
109 connection.getOutputStream().write(paramBytes);
110
111 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
112
113 StringBuilder responseBuilder = new StringBuilder();
114 String temp;
115
116 while ((temp = reader.readLine()) != null)
117 {
118 responseBuilder.append(temp);
119 }
120
121 reader.close();
122 connection.disconnect();
123
124 ObjectMapper mapper = new ObjectMapper();
125
126 JsonNode node = mapper.readTree(responseBuilder.toString());
127
128 String access_token = node.get("access_token") == null ? "미동의" : node.get("access_token").textValue();
129 String token_type = node.get("token_type") == null ? "미동의" : node.get("token_type").textValue();
130 String scope = node.get("scope") == null ? "미동의" : node.get("scope").textValue();
131
132 return new OAuth2AccessToken(access_token, token_type, 0, null, scope, responseBuilder.toString());
133 }
134
135 /**
136 * 사용자 정보 응답 반환 메서드
137 *
138 * @param access: [String] 접근 토큰
139 *
140 * @return [Response] 사용자 정보 응답
141 *
142 * @throws IOException 데이터 입출력 예외
143 * @throws ExecutionException 실행 예외
144 * @throws InterruptedException 인터럽트 예외
145 */
146 @Override
147 public Response getUserInfo(String access) throws IOException, ExecutionException, InterruptedException
148 {
149 OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, getUserInfoEndPoint());
150 oAuthRequest.addHeader("Authorization", Util.builder("token ", access));
151
152 service.signRequest(access, oAuthRequest);
153
154 return service.execute(oAuthRequest);
155 }
156
157 /**
158 * 유저 정보 객체 반환 메서드
159 *
160 * @param body: [String] OAuth 응답 내용
161 *
162 * @return [UserInfoBean] 유저 정보 객체
163 *
164 * @throws JsonProcessingException JSON 파싱 예외
165 */
166 @Override
167 public UserInfoBean getUserInfoBean(String body) throws JsonProcessingException
168 {
169 ObjectMapper mapper = new ObjectMapper();
170
171 JsonNode node = mapper.readTree(body);
172
173 String email = node.get("email") == null ? "미동의" : node.get("email").textValue();
174 String name = node.get("name") == null ? "미동의" : node.get("name").textValue();
175 String picture = node.get("avatar_url") == null ? "/oauth2/assets/images/logo.png" : node.get("avatar_url").textValue();
176
177 return new UserInfoBean(email, name, picture, MODULE_NAME);
178 }
179
180 /**
181 * 연동 해제 결과 반환 메서드
182 *
183 * @param access: [String] 접근 토큰
184 *
185 * @return [boolean] 연동 해제 결과
186 *
187 * @throws IOException 데이터 입출력 예외
188 */
189 @Override
190 public boolean deleteInfo(String access) throws IOException
191 {
192 HashMap<String, String> params = new HashMap<>();
193 params.put("access_token", access);
194
195 ObjectMapper mapper = new ObjectMapper();
196
197 byte[] paramBytes = mapper.writeValueAsString(params).getBytes(StandardCharsets.UTF_8);
198
199 URL url = new URL(Util.builder("https://api.github.com/applications/", API_KEY, "/grant"));
200
201 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
202 connection.setRequestMethod("DELETE");
203 connection.addRequestProperty("Authorization", Util.builder("Basic ", Base64.getEncoder().encodeToString(Util.builder(API_KEY, ":", SECRET_KEY).getBytes())));
204 connection.addRequestProperty("Accept", "application/vnd.github.v3+json");
205 connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
206 connection.setDoOutput(true);
207 connection.getOutputStream().write(paramBytes);
208
209 int status = connection.getResponseCode();
210
211 connection.disconnect();
212
213 return status == 204;
214 }
215
216 /**
217 * 정보 제공 동의 갱신 URL 반환 메서드
218 *
219 * @param state: [String] 고유 상태값
220 *
221 * @return [String] 정보 제공 동의 갱신 URL
222 */
223 @Override
224 public String getUpdateAuthorizationUrl(String state)
225 {
226 return null;
227 }
228
229 /**
230 * 접근 토큰 요청 URL 반환 메서드
231 *
232 * @return [String] 접근 토큰 요청 URL
233 */
234 @Override
235 public String getAccessTokenEndpoint()
236 {
237 return "https://github.com/login/oauth/access_token";
238 }
239
240 /**
241 * 인증 API 요청 URL 반환 메서드
242 *
243 * @return [String] 인증 API 요청 URL
244 */
245 @Override
246 protected String getAuthorizationBaseUrl()
247 {
248 return "https://github.com/login/oauth/authorize";
249 }
250
251 /**
252 * 사용자 정보 요청 URL 반환 메서드
253 *
254 * @return [String] 사용자 정보 요청 URL
255 */
256 @Override
257 protected String getUserInfoEndPoint()
258 {
259 return "https://api.github.com/user";
260 }
261}

정리한 전체 코드는 위와 같다.

정리 🔗

이로써 모든 플랫폼에 대한 인증 모듈 구현이 끝났다. AuthModule를 활용함으로써 최소한의 코드로 각 플랫폼에 대응하는 모듈을 구현했다. 만약 추후 다른 OAuth를 붙일 경우, 위와 같은 방식으로 모듈을 추가 구성하면 된다.

다음 장에서는 모듈을 호출해서 사용하는 영역인 프로세스를 구현할 예정이다.