2단계. 로그인
사용자가 별도의 회원가입 없이 X(Twitter) 계정으로 앱에 로그인하도록 구현합니다. 시작하기 전에 1단계. 연동 환경 구성을 마치세요.
X는 전용 Add-on을 제공하지 않으므로 모든 OS에서 웹 로그인 세션으로 X 로그인 페이지를 엽니다. 웹 로그인 세션은 X 인가 코드만 반환합니다. 외부 인증 코드 교환으로 인가 코드를 교환한 뒤 로그인하는 Authorization Code 흐름을 따릅니다.
X는 PKCE를 필수로 요구합니다. 인가 URL을 만들기 전에 PKCE 값 한 쌍을 생성하고, codeVerifier는 외부 인증 코드 교환 단계까지 앱에 보관하세요.
1. X 자격 증명 획득
웹 로그인 세션으로 X 로그인 페이지를 열고 인가 코드를 받습니다. 인가 URL 구성과 응답 파라미터는 X가 정한 OAuth 2.0 규격을 따르므로, 필요한 파라미터는 X 개발자 문서에서 확인하세요.
인가 URL 구성
인가 URL에 아래 값을 넣어야 로그인이 정상 동작합니다.
- X Developer Portal에서 확인한 Client ID
- X Developer Portal의 허용 목록에 등록한 리다이렉트 URI
- PKCE
code_challenge - 로그인을 시도할 때마다 새로 만든 난수
state
state는 앱이 시작하지 않은 인증 결과를 걸러 내는 값이므로, 만든 값을 보관해 두었다가 콜백 확인에서 비교하세요.
콜백 확인
웹 로그인 세션은 콜백 파라미터를 그대로 돌려줄 뿐 검증하지 않으므로, 콜백을 받으면 state부터 확인하세요. 콜백의 state가 보관한 값과 다르면 이번 로그인에서 시작한 응답이 아니므로 로그인을 중단합니다. state가 같더라도 error가 있거나 code가 없으면 로그인을 중단합니다. error가 access_denied이면 사용자가 로그인을 거절한 경우이고, 그 밖의 error는 로그인에 실패한 경우입니다.
using Hive.Axyl.Auth.Addon.WebAuth;
using Hive.Axyl.Core;
if (!HiveCore.TryResolve<IExternalUserAgent>(out var webAuth))
{
return;
}
// X용 PKCE 값은 인가 URL을 만들기 전에 생성해 보관합니다.
var (xVerifier, xChallenge) = CreatePkce();
// 로그인을 시도할 때마다 새로 만들어 보관합니다.
string state = CreateNonce();
// 인가 URL은 X OAuth 2.0 규격에 맞춰 앱이 만듭니다.
string authorizationUrl = BuildXAuthorizationUrl(xChallenge, state, redirectUri);
var sessionResult = await webAuth.OpenAsync(new OpenRequest {
Url = authorizationUrl,
RedirectUri = redirectUri,
});
if (sessionResult is not ExternalUserAgentServiceOpenResult.Success session)
{
// UserCanceled와 Failure 처리는 [웹 로그인 세션](web-auth-session.md) 참조
return;
}
var callback = session.Data.Parameters;
// state를 가장 먼저 확인합니다.
if (!callback.TryGetValue("state", out var returnedState) || returnedState != state)
{
// 이번 로그인에서 시작한 응답이 아님 → 로그인 중단
return;
}
if (callback.TryGetValue("error", out var xError))
{
// "access_denied"이면 사용자가 거절함 → 로그인 화면 유지
// 그 밖의 값이면 로그인 실패 → 로그인 중단
return;
}
if (!callback.TryGetValue("code", out var xCode))
{
// 인가 코드가 없음 → 로그인 중단
return;
}
CreatePkce()는 게스트 계정 생성에서 정의한 헬퍼이고, CreateNonce()는 Apple로 로그인에서 정의한 난수 생성 헬퍼입니다. BuildXAuthorizationUrl()은 앱이 직접 구현하는 인가 URL 생성 코드입니다.
2. 외부 인증 코드 교환
받은 인가 코드를 외부 인증 코드 교환으로 보내 로그인에 사용할 자격 증명으로 바꿉니다. 교환 요청에는 아래 값을 넣습니다.
ProviderId:Provider.XProviderCode: 콜백의codeRedirectUri: 인가 URL의redirect_uri에 넣은 값과 한 글자도 다르지 않은 값CodeVerifier: 인가 URL을 만들기 전에 생성해 보관해 둔codeVerifier. X에서는 필수
using Hive.Axyl.Auth;
using Hive.Axyl.Core;
IAuthService auth = HiveCore.Resolve<IAuthService>();
var exchange = await auth.ExchangeProviderTokenAsync(new ProviderTokenRequest {
ProviderId = Provider.X,
ProviderCode = xCode,
RedirectUri = redirectUri, // 인가 URL의 redirect_uri와 같은 값
CodeVerifier = xVerifier, // X는 PKCE가 필수입니다.
});
if (exchange is not AuthExchangeProviderTokenResult.Success exchanged)
{
// 교환 실패 처리는 [외부 인증 코드 교환](provider-token-exchange.md) 참조
return;
}
string xProviderToken = exchanged.Data.ProviderToken; // 로그인 요청의 ProviderToken
string xProviderUserId = exchanged.Data.ProviderUserId; // 로그인 요청의 ProviderUserId
3. 외부 인증 제공자 로그인
교환으로 얻은 xProviderUserId와 xProviderToken으로 외부 인증 제공자 로그인을 호출합니다. ProviderId에는 Provider.X를 지정합니다.
using Hive.Axyl.Auth;
using Hive.Axyl.Core;
IAuthService auth = HiveCore.Resolve<IAuthService>();
// X용 PKCE 값과 별개인 Axyl 인증 서버용 PKCE 값을 새로 만듭니다.
var (codeVerifier, codeChallenge) = CreatePkce();
var result = await auth.LoginProviderAsync(new ProviderLoginRequest {
ProviderId = Provider.X,
ProviderUserId = xProviderUserId,
ProviderToken = xProviderToken,
DeviceKey = deviceKey,
ClientId = "{clientId}",
CodeChallenge = codeChallenge,
CodeChallengeMethod = CodeChallengeMethod.S256,
});
if (result is AuthLoginProviderResult.Success success)
{
// 로그인 성공 → 토큰을 발급하고 세션을 활성화합니다.
await StartSessionAsync(success.Data.AuthorizationCode, codeVerifier, success.Data.PlayerId);
}
// 그 밖의 응답 케이스와 전체 호출 파라미터는 [외부 인증 제공자 로그인](provider-login.md) 참조
StartSessionAsync()의 정의와 세션 활성화 절차는 토큰 발급과 세션 활성화를 참조하세요.