Android (Kotlin/Java) 애플리케이션에 인증 (Authentication)을 추가하세요
이 가이드는 Android 애플리케이션에 Logto를 통합하는 방법을 보여줍니다.
- 예제는 View 시스템 및 View Model을 기반으로 하지만, Jetpack Compose를 사용할 때도 개념은 동일합니다.
- 예제는 Kotlin으로 작성되었지만, 개념은 Java에서도 동일합니다.
- Kotlin 및 Java 샘플 프로젝트는 우리의 SDK 저장소에서 사용할 수 있습니다.
- 튜토리얼 비디오는 우리의 YouTube 채널에서 볼 수 있습니다.
사전 준비 사항
- Logto Cloud 계정 또는 셀프 호스팅 Logto.
- Logto 네이티브 애플리케이션 생성.
- Kotlin Android 애플리케이션 프로젝트.
설치
Logto Android SDK의 최소 지원 Android API 레벨은 레벨 24입니다.
Logto Android SDK는 두 가지 주요 버전으로 제공됩니다:
- v2: 네이티브 소셜 커넥터에 필요한 임베디드 WebView에서 로그인 환경을 엽니다. 단, 패스키 로그인은 지원하지 않습니다 (WebView는 패스키의 기반 표준인 WebAuthn을 지원하지 않습니다).
- v3 (beta): Chrome Custom Tabs (시스템 브라우저)에서 로그인 환경을 열어 패스키 로그인을 지원하고 브라우저 세션을 공유합니다. v3은 WeChat (Native) 및 Alipay (Native) 커넥터 지원을 제거합니다. 대신 브라우저를 통해 작동하는 WeChat (Web) 및 Alipay (Web)을 사용할 수 있습니다. 네이티브 커넥터에 의존하는 경우 v2를 유지하세요.
이 가이드는 두 버전을 모두 다룹니다. 아래 탭에서 버전을 선택하면, 선택이 이 가이드 전반에 걸쳐 동기화됩니다.
Logto Android SDK를 설치하기 전에, Gradle 프로젝트 빌드 파일의 저장소 구성에 mavenCentral()이 추가되어 있는지 확인하세요:
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
Logto Android SDK를 종속성에 추가하세요:
- v2
- v3 (beta)
- Kotlin
- Groovy
dependencies {
implementation("io.logto.sdk:android:2.0.3")
}
dependencies {
implementation 'io.logto.sdk:android:2.0.3'
}
v3은 GA 전까지 3.0.0-beta 프리릴리스로 제공됩니다. 최신 프리릴리스를 버전으로 사용하세요:
- Kotlin
- Groovy
dependencies {
implementation("io.logto.sdk:android:3.0.0-beta")
}
dependencies {
implementation 'io.logto.sdk:android:3.0.0-beta'
}
SDK가 인터넷에 접근해야 하므로, AndroidManifest.xml 파일에 다음 권한을 추가해야 합니다:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 인터넷 권한 추가 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 기타 구성... -->
</manifest>
통합
LogtoClient 초기화
LogtoViewModel.kt 파일을 생성하고 이 뷰 모델에서 LogtoClient를 초기화하세요:
//...다른 import와 함께
import io.logto.sdk.android.LogtoClient
import io.logto.sdk.android.type.LogtoConfig
class LogtoViewModel(application: Application) : AndroidViewModel(application) {
private val logtoConfig = LogtoConfig(
endpoint = "<your-logto-endpoint>",
appId = "<your-app-id>",
scopes = null,
resources = null,
usingPersistStorage = true,
)
private val logtoClient = LogtoClient(logtoConfig, application)
companion object {
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
// extras에서 Application 객체를 가져옵니다
val application = checkNotNull(extras[APPLICATION_KEY])
return LogtoViewModel(application) as T
}
}
}
}
그런 다음, MainActivity.kt에 대한 LogtoViewModel을 생성하세요:
//...다른 import와 함께
class MainActivity : AppCompatActivity() {
private val logtoViewModel: LogtoViewModel by viewModels { LogtoViewModel.Factory }
//...다른 코드
}
리디렉트 URI 구성
자세한 내용을 살펴보기 전에, 최종 사용자 경험에 대한 간단한 개요를 소개합니다. 로그인 과정은 다음과 같이 단순화할 수 있습니다:
- 귀하의 앱이 로그인 메서드를 호출합니다.
- 사용자는 Logto 로그인 페이지로 리디렉션됩니다. 네이티브 앱의 경우, 시스템 브라우저가 열립니다.
- 사용자가 로그인하면, 다시 귀하의 앱(리디렉션 URI로 설정됨)으로 리디렉션됩니다.
리디렉션 기반 로그인에 관하여
- 이 인증 과정은 OpenID Connect (OIDC) 프로토콜을 따르며, Logto는 사용자 로그인을 보호하기 위해 엄격한 보안 조치를 시행합니다.
- 여러 앱이 있는 경우, 동일한 아이덴티티 제공자 (Logto)를 사용할 수 있습니다. 사용자가 한 앱에 로그인하면, Logto는 사용자가 다른 앱에 접근할 때 자동으로 로그인 과정을 완료합니다.
리디렉션 기반 로그인에 대한 이론적 배경과 이점에 대해 더 알고 싶다면, Logto 로그인 경험 설명을 참조하세요.
Logto 콘솔의 애플리케이션 세부 정보 페이지로 전환하세요. 리디렉션 URI io.logto.android://io.logto.sample/callback를 추가하고 "변경 사항 저장"을 클릭하세요.
Android에서는 리디렉트 URI가 다음 패턴을 따릅니다: $(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback:
LOGTO_REDIRECT_SCHEME는 역도메인 형식의 사용자 정의 스킴이어야 합니다.YOUR_APP_PACKAGE는 애플리케이션 패키지 이름입니다.
io.logto.android를 사용자 정의 LOGTO_REDIRECT_SCHEME로, io.logto.sample을 애플리케이션 패키지 이름으로 처리한다고 가정하면, 리디렉트 URI는 io.logto.android://io.logto.sample/callback이어야 합니다.
- v2
- v3 (beta)
추가 설정이 필요하지 않습니다. 로그인 환경은 임베디드 WebView에서 열리며, SDK가 WebView 내에서 리디렉션을 가로챕니다.
v3에서는 로그인 환경이 Custom Tab (시스템 브라우저)에서 열리며, 리디렉션은 OS 수준의 인텐트 필터를 통해 앱으로 다시 라우팅됩니다. 앱의 빌드 파일에서 logtoRedirectScheme 매니페스트 플레이스홀더를 사용하여 리디렉트 URI의 스킴을 선언해야 합니다:
- Kotlin
- Groovy
android {
defaultConfig {
manifestPlaceholders["logtoRedirectScheme"] = "io.logto.android"
}
}
android {
defaultConfig {
manifestPlaceholders.logtoRedirectScheme = 'io.logto.android'
}
}
또한 v3은 Android의 인텐트 필터 매칭을 통해 리디렉트 URI 패턴을 강제하므로, 패턴에서 벗어난 리디렉트 URI는 앱으로 전달되지 않습니다:
- 스킴은
logtoRedirectScheme매니페스트 플레이스홀더와 일치해야 합니다. - 호스트는
applicationId여야 합니다. - 경로는
/callback이어야 합니다.
인텐트 필터 매칭은 대소문자를 구분하고 브라우저는 스킴을 소문자로 변환하므로, 스킴과 호스트는 소문자로 유지하세요.
커스텀 스킴 대신 App Links를 사용하시겠습니까?
커스텀 스킴 대신 Android App Links(https 리디렉트 URI를 자신이 소유한 도메인에 사용)를 사용하려면:
-
Digital Asset Links 파일을
https://your.domain/.well-known/assetlinks.json에 호스팅하고, 애플리케이션 ID와 서명 인증서의 SHA-256 지문을 선언하세요. Play App Signing으로 게시하는 경우, Play Console의 설정 > 앱 서명에서 릴리스 지문을 찾을 수 있습니다. 파일은Content-Type: application/json으로 HTTP 200과 리디렉션 없이 제공되어야 합니다. -
SDK의 리디렉트 수신 액티비티
io.logto.sdk.android.auth.logto.LogtoRedirectReceiverActivity에 App Links 인텐트 필터를AndroidManifest.xml파일에 선언하세요. 커스텀 스킴을 전혀 사용하지 않는 경우,tools:node="removeAll"로 SDK의 기본 제공 필터를 제거하면logtoRedirectScheme매니페스트 플레이스홀더가 더 이상 필요하지 않습니다:AndroidManifest.xml<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><application><activity android:name="io.logto.sdk.android.auth.logto.LogtoRedirectReceiverActivity"><!-- 커스텀 스킴 리디렉션을 함께 유지하려면 이 줄을 생략하세요. --><intent-filter tools:node="removeAll" /><intent-filter android:autoVerify="true"><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="https" android:host="your.domain" android:path="/callback" /></intent-filter></activity></application></manifest> -
Logto Console의 애플리케이션 세부 정보 페이지에서
https://your.domain/callback을 리디렉트 URI (로그아웃에도 사용하는 경우 로그아웃 후 리디렉트 URI)로 추가하고,signIn/signOut에 전달하세요.
콜백이 이제 도메인의 실제 URL이므로, 서버 리디렉션에서 App Links를 실행하지 않는 브라우저를 위해 대체 페이지(예: "앱으로 돌아가기" 버튼)를 제공하세요. 버튼은 현재 URL만 링크하면 됩니다(예: href를 window.location.href로 설정): 인증 매개변수는 쿼리 문자열에 있으며, 사용자가 클릭하면 동일한 URL이 앱으로 라우팅될 수 있는 기회를 다시 얻습니다. Android 12+에서는 미인증 도메인이 앱을 열지 않으므로, assetlinks.json이 잘못되면 조용히 실패합니다. adb shell pm get-app-links <applicationId>로 인증 상태를 확인할 수 있습니다.
로그인 및 로그아웃 구현
logtoClient.signIn을 호출하기 전에, Admin Console 에서 Redirect URI 를 올바르게 구성했는지
확인하세요. :::
logtoClient.signIn을 사용하여 사용자를 로그인시키고 logtoClient.signOut을 사용하여 사용자를 로그아웃시킬 수 있습니다.
- v2
- v3 (beta)
예를 들어, Android 앱에서:
//...다른 import와 함께
class LogtoViewModel(application: Application) : AndroidViewModel(application) {
// ...다른 코드
// 인증 상태를 관찰하기 위한 라이브 데이터를 추가합니다
private val _authenticated = MutableLiveData(logtoClient.isAuthenticated)
val authenticated: LiveData<Boolean>
get() = _authenticated
fun signIn(context: Activity) {
logtoClient.signIn(context, "io.logto.android://io.logto.sample/callback") { logtoException ->
logtoException?.let { println(it) }
// 라이브 데이터를 업데이트합니다
_authenticated.postValue(logtoClient.isAuthenticated)
}
}
fun signOut() {
logtoClient.signOut { logtoException ->
logtoException?.let { println(it) }
// 라이브 데이터를 업데이트합니다
_authenticated.postValue(logtoClient.isAuthenticated)
}
}
}
그런 다음, 활동에서 signIn 및 signOut 메서드를 호출합니다:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//...다른 코드
// 레이아웃에 "sign_in_button"이라는 id를 가진 버튼이 있다고 가정합니다
val signInButton = findViewById<Button>(R.id.sign_in_button)
signInButton.setOnClickListener {
logtoViewModel.signIn(this)
}
// 레이아웃에 "sign_out_button"이라는 id를 가진 버튼이 있다고 가정합니다
val signOutButton = findViewById<Button>(R.id.sign_out_button)
signOutButton.setOnClickListener {
if (logtoViewModel.authenticated) { // 사용자가 인증되었는지 확인합니다
logtoViewModel.signOut()
}
}
// UI를 업데이트하기 위해 인증 상태를 관찰합니다
logtoViewModel.authenticated.observe(this) { authenticated ->
if (authenticated) {
// 사용자가 인증되었습니다
signInButton.visibility = View.GONE
signOutButton.visibility = View.VISIBLE
} else {
// 사용자가 인증되지 않았습니다
signInButton.visibility = View.VISIBLE
signOutButton.visibility = View.GONE
}
}
}
}
v3에서 logtoClient.signOut은 완전한 로그아웃을 수행합니다: 로컬 자격 증명을 지우고, 리프레시 토큰을 폐기하며, 브라우저에서 세션 종료 엔드포인트를 열어 Logto 세션을 종료합니다. 브라우저는 로그아웃 후 리디렉트 URI를 통해 앱으로 돌아갑니다. 사용하기 전에 Logto Console의 애플리케이션 세부 정보 페이지로 이동하여, 로그아웃 후 리디렉트 URI io.logto.android://io.logto.sample/callback을 추가하고 "변경 사항 저장"을 클릭하세요. 로그아웃 후 리디렉트 URI는 리디렉트 URI와 동일한 패턴을 따르며, 스킴도 logtoRedirectScheme 매니페스트 플레이스홀더와 일치해야 합니다.
예를 들어, Android 앱에서:
//...다른 import와 함께
class LogtoViewModel(application: Application) : AndroidViewModel(application) {
// ...다른 코드
// 인증 상태를 관찰하기 위한 라이브 데이터를 추가합니다
private val _authenticated = MutableLiveData(logtoClient.isAuthenticated)
val authenticated: LiveData<Boolean>
get() = _authenticated
fun signIn(context: Activity) {
logtoClient.signIn(context, "io.logto.android://io.logto.sample/callback") { logtoException ->
logtoException?.let { println(it) }
// 라이브 데이터를 업데이트합니다
_authenticated.postValue(logtoClient.isAuthenticated)
}
}
fun signOut(context: Activity) {
logtoClient.signOut(context, "io.logto.android://io.logto.sample/callback") { logtoException ->
logtoException?.let { println(it) }
// 라이브 데이터를 업데이트합니다
_authenticated.postValue(logtoClient.isAuthenticated)
}
}
}
그런 다음, 활동에서 signIn 및 signOut 메서드를 호출합니다:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//...다른 코드
// 레이아웃에 "sign_in_button"이라는 id를 가진 버튼이 있다고 가정합니다
val signInButton = findViewById<Button>(R.id.sign_in_button)
signInButton.setOnClickListener {
logtoViewModel.signIn(this)
}
// 레이아웃에 "sign_out_button"이라는 id를 가진 버튼이 있다고 가정합니다
val signOutButton = findViewById<Button>(R.id.sign_out_button)
signOutButton.setOnClickListener {
if (logtoViewModel.authenticated) { // 사용자가 인증되었는지 확인합니다
logtoViewModel.signOut(this)
}
}
// UI를 업데이트하기 위해 인증 상태를 관찰합니다
logtoViewModel.authenticated.observe(this) { authenticated ->
if (authenticated) {
// 사용자가 인증되었습니다
signInButton.visibility = View.GONE
signOutButton.visibility = View.VISIBLE
} else {
// 사용자가 인증되지 않았습니다
signInButton.visibility = View.VISIBLE
signOutButton.visibility = View.GONE
}
}
}
}
- 로그아웃 후 리디렉트 URI 없이
logtoClient.signOut(context)를 호출할 수도 있습니다. 이 경우 Console 설정이 필요하지 않습니다: 브라우저에 Logto 로그아웃 페이지가 표시되며, 사용자는 직접 닫아서 앱으로 돌아옵니다. - UI 컨텍스트를 사용할 수 없는 경우,
logtoClient.clearCredentials를 호출하여 로컬 자격 증명을 지우고 리프레시 토큰을 폐기할 수 있습니다. 이 경우 브라우저의 Logto 세션은 유지되므로, 다음signIn시 해당 세션을 통해 자동으로 로그인될 수 있습니다.
체크포인트: 애플리케이션 테스트하기
이제 애플리케이션을 테스트할 수 있습니다:
- 애플리케이션을 실행하면 로그인 버튼이 표시됩니다.
- 로그인 버튼을 클릭하면 SDK가 로그인 프로세스를 초기화하고 Logto 로그인 페이지로 리디렉션됩니다.
- 로그인 후, 애플리케이션으로 다시 리디렉션되어 로그아웃 버튼이 표시됩니다.
- 로그아웃 버튼을 클릭하여 토큰 저장소를 지우고 로그아웃합니다.
사용자 정보 가져오기
사용자 정보 표시
사용자의 정보를 표시하려면 logtoClient.getIdTokenClaims() 메서드를 사용할 수 있습니다. 예를 들어, ViewModel에서 사용자 정보를 가져와서 활동에 표시할 수 있습니다:
class LogtoViewModel(application: Application) : AndroidViewModel(application) {
// ...다른 코드
// ID 토큰 클레임을 관찰하기 위한 라이브 데이터를 추가합니다
private val _idTokenClaims = MutableLiveData<IdTokenClaims>()
val idTokenClaims: LiveData<IdTokenClaims>
get() = _idTokenClaims
fun getIdTokenClaims() {
logtoClient.getIdTokenClaims { logtoException, idTokenClaims ->
logtoException?.let { _logtoException.postValue(it) } ?: _idTokenClaims.postValue(idTokenClaims)
}
}
}
//...다른 import와 함께
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//...다른 코드
// 레이아웃에 `user_info_text_view` ID를 가진 텍스트 뷰가 있다고 가정합니다
val userInfoResponseTextView: TextView = findViewById(R.id.user_info_text_view)
logtoViewModel.userInfoResponse.observe(this) { userInfoResponse ->
userInfoResponseTextView.text = if (userInfoResponse !== null) {
val json = Gson().toJson(userInfoResponse, UserInfoResponse::class.java)
JSONObject(json).toString(2)
} else {
""
}
}
}
}
추가 클레임 요청
logtoClient.getIdTokenClaims()에서 반환된 객체에 일부 사용자 정보가 누락된 것을 발견할 수 있습니다.
이는 OAuth 2.0 및 OpenID Connect (OIDC)가 최소 권한 원칙 (PoLP)을 따르도록 설계되었기 때문이며,
Logto는 이러한 표준을 기반으로 구축되었습니다.
기본적으로 제한된 클레임 (Claim)만 반환됩니다. 더 많은 정보를 원하시면, 추가적인 스코프 (Scope)를 요청하여 더 많은 클레임에 접근할 수 있습니다.
"클레임 (Claim)"은 주체에 대해 주장하는 내용이며, "스코프 (Scope)"는 클레임의 그룹입니다. 현재의 경우, 클레임은 사용자에 대한 정보입니다.
다음은 스코프 - 클레임 관계의 비규범적 예시입니다:
"sub" 클레임은 "주체"를 의미하며, 이는 사용자의 고유 식별자 (즉, 사용자 ID)입니다.
Logto SDK는 항상 세 가지 스코프를 요청합니다: openid, profile, 그리고 offline_access.
추가 스코프를 요청하려면, LogtoConfig 객체에 스코프를 전달할 수 있습니다. 예를 들어:
private val logtoConfig = LogtoConfig(
// ...다른 설정
scopes = listOf("email", "phone"), // 또는 `listOf(UserScope.EMAIL, UserScope.PHONE)`
)
그런 다음 logtoClient.getIdTokenClaims()의 반환 값에서 추가 클레임에 접근할 수 있습니다:
logtoClient.getIdTokenClaims { logtoException, idTokenClaims ->
println("IdTokenClaims:$idTokenClaims")
}
// 이제 추가 클레임 `claims.email`, `claims.phone` 등에 접근할 수 있습니다.
네트워크 요청이 필요한 클레임
ID 토큰의 비대화를 방지하기 위해, 일부 클레임은 네트워크 요청을 통해 가져와야 합니다. 예를 들어, custom_data 클레임은 스코프에서 요청되더라도 사용자 객체에 포함되지 않습니다. 이러한 클레임에 접근하려면, logtoClient.fetchUserInfo() 메서드를 사용할 수 있습니다:
logtoClient.fetchUserInfo {_, userInfoResponse ->
println("UserInfoResponse:$userInfoResponse")
}
// 이제 클레임 `userInfo.custom_data`에 접근할 수 있습니다.
스코프와 클레임
Logto는 OIDC 스코프 (Scope) 및 클레임 (Claim) 규칙을 사용하여 ID 토큰 및 OIDC userinfo 엔드포인트에서 사용자 정보를 가져오기 위한 스코프 (Scope)와 클레임 (Claim)을 정의합니다. "스코프 (Scope)"와 "클레임 (Claim)" 모두 OAuth 2.0 및 OpenID Connect (OIDC) 명세에서 온 용어입니다.
표준 OIDC 클레임 (Claim)의 경우, ID 토큰에 포함되는지는 요청된 스코프 (Scope)에 의해 엄격하게 결정됩니다. 확장 클레임 (예: custom_data 및 organizations)은 커스텀 ID 토큰 설정을 통해 ID 토큰에 추가로 포함되도록 구성할 수 있습니다.
지원되는 스코프와 해당 클레임(Claim)의 목록은 다음과 같습니다:
표준 OIDC 스코프
openid (기본값)
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
| sub | string | 사용자의 고유 식별자 |
profile (기본값)
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
| name | string | 사용자의 전체 이름 |
| username | string | 사용자의 사용자명 |
| picture | string | 최종 사용자의 프로필 사진 URL. 이 URL은 이미지 파일(예: PNG, JPEG, GIF 이미지 파일)을 가리켜야 하며, 이미지를 포함한 웹 페이지가 아니어야 합니다. 이 URL은 최종 사용자를 설명할 때 표시하기에 적합한 프로필 사진을 명확히 참조해야 하며, 최종 사용자가 임의로 촬영한 사진이 아니어야 합니다. |
| created_at | number | 최종 사용자가 생성된 시간. 시간은 Unix epoch (1970-01-01T00:00:00Z) 이후 밀리초로 표시됩니다. |
| updated_at | number | 최종 사용자의 정보가 마지막으로 업데이트된 시간. 시간은 Unix epoch (1970-01-01T00:00:00Z) 이후 밀리초로 표시됩니다. |
기타 표준 클레임(Claim)에는 family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, locale 등이 있으며, 이들은 userinfo 엔드포인트를 요청하지 않아도 profile 스코프에 포함됩니다. 위의 클레임과의 차이점은, 이 클레임들은 값이 비어 있지 않을 때만 반환되며, 위의 클레임들은 값이 비어 있으면 null을 반환합니다.
표준 클레임(Claim)과 달리, created_at 및 updated_at 클레임은 초 단위가 아닌 밀리초 단위를 사용합니다.
email
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
string | 사용자의 이메일 주소 | |
| email_verified | boolean | 이메일 주소가 인증되었는지 여부 |
phone
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
| phone_number | string | 사용자의 전화번호 |
| phone_number_verified | boolean | 전화번호가 인증되었는지 여부 |
address
주소 클레임(Claim)의 세부 사항은 OpenID Connect Core 1.0 을 참조하세요.
**(기본값)**으로 표시된 스코프는 항상 Logto SDK에서 요청합니다. 표준 OIDC 스코프의 클레임(Claim)은 해당 스코프가 요청될 때 항상 ID 토큰 (ID token)에 포함되며, 비활성화할 수 없습니다.
확장 스코프
다음 스코프는 Logto에서 확장한 것으로, userinfo 엔드포인트를 통해 클레임(Claim)을 반환합니다. 이 클레임들은 Console > Custom JWT를 통해 ID 토큰 (ID token)에 직접 포함되도록 설정할 수도 있습니다. 자세한 내용은 커스텀 ID 토큰을 참고하세요.
custom_data
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| custom_data | object | 사용자의 커스텀 데이터 |
identities
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| identities | object | 사용자의 연결된 아이덴티티 | |
| sso_identities | array | 사용자의 연결된 SSO 아이덴티티 |
roles
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| roles | string[] | 사용자의 역할 (Role) | ✅ |
urn:logto:scope:organizations
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| organizations | string[] | 사용자가 속한 조직 (Organization) ID | ✅ |
| organization_data | object[] | 사용자가 속한 조직 (Organization) 데이터 |
이러한 조직 (Organization) 클레임(Claim)은 불투명 토큰 (Opaque token)을 사용할 때도 userinfo 엔드포인트를 통해 조회할 수 있습니다. 그러나 불투명 토큰 (Opaque token)은 조직 토큰 (Organization token)으로 사용되어 조직별 리소스에 접근할 수 없습니다. 자세한 내용은 불투명 토큰 (Opaque token)과 조직 (Organization)을 참고하세요.
urn:logto:scope:organization_roles
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| organization_roles | string[] | 사용자가 속한 조직 (Organization)의 역할 (Role), 형식: <organization_id>:<role_name> | ✅ |
API 리소스 및 조직
먼저 🔐 역할 기반 접근 제어 (RBAC)를 읽어 Logto RBAC의 기본 개념과 API 리소스를 적절히 설정하는 방법을 이해하는 것을 권장합니다.
Logto 클라이언트 구성
API 리소스를 설정한 후, 애플리케이션에서 Logto를 구성할 때 이를 추가할 수 있습니다:
val logtoConfig = LogtoConfig(
//...other configs
resources = listOf("https://shopping.your-app.com/api", "https://store.your-app.com/api"), // API 리소스를 추가하세요
)
각 API 리소스는 자체 권한 (스코프)을 가지고 있습니다.
예를 들어, https://shopping.your-app.com/api 리소스는 shopping:read 및 shopping:write 권한을 가지고 있으며, https://store.your-app.com/api 리소스는 store:read 및 store:write 권한을 가지고 있습니다.
이러한 권한을 요청하려면, 애플리케이션에서 Logto를 구성할 때 추가할 수 있습니다:
val logtoConfig = LogtoConfig(
// ..other configs
scopes = listOf("shopping:read", "shopping:write", "store:read", "store:write"),
resources = listOf("https://shopping.your-app.com/api", "https://store.your-app.com/api"),
)
스코프가 API 리소스와 별도로 정의된 것을 알 수 있습니다. 이는 OAuth 2.0을 위한 리소스 지표가 요청의 최종 스코프가 모든 대상 서비스의 모든 스코프의 데카르트 곱이 될 것이라고 명시하기 때문입니다.
따라서 위의 경우, Logto에서 정의된 스코프를 단순화할 수 있으며, 두 API 리소스 모두 접두사 없이 read 및 write 스코프를 가질 수 있습니다. 그런 다음, Logto 구성에서:
val logtoConfig = LogtoConfig(
// ...other configs
scopes = listOf("read", "write"),
resources = listOf("https://shopping.your-app.com/api", "https://store.your-app.com/api"),
)
모든 API 리소스에 대해 read 및 write 스코프를 요청하게 됩니다.
API 리소스에 정의되지 않은 스코프를 요청해도 괜찮습니다. 예를 들어, API 리소스에 email 스코프가 없더라도 email 스코프를 요청할 수 있습니다. 사용 불가능한 스코프는 안전하게 무시됩니다.
성공적으로 로그인한 후, Logto는 사용자의 역할에 따라 API 리소스에 적절한 스코프를 발급합니다.
API 리소스를 위한 액세스 토큰 가져오기
특정 API 리소스에 대한 액세스 토큰을 가져오려면 getAccessToken 메서드를 사용할 수 있습니다:
logtoClient.getAccessToken("https://shopping.your-app.com/api") { logtoException, accessToken ->
logtoException?.let { println(it) }
accessToken?.let { println(it) }
}
이 메서드는 사용자가 관련 권한을 가지고 있을 때 API 리소스에 접근할 수 있는 JWT 액세스 토큰을 반환합니다. 현재 캐시된 액세스 토큰이 만료된 경우, 이 메서드는 자동으로 리프레시 토큰을 사용하여 새로운 액세스 토큰을 얻으려고 시도합니다.
조직 토큰 가져오기
조직이 처음이라면, 시작하기 위해 🏢 조직 (다중 테넌시)을 읽어보세요.
Logto 클라이언트를 구성할 때 UserScope.Organizations 스코프를 추가해야 합니다:
val logtoConfig = LogtoConfig(
// ...other configs
scopes = listOf(UserScope.Organizations),
)
사용자가 로그인하면, 사용자에 대한 조직 토큰을 가져올 수 있습니다:
// 매개변수를 유효한 조직 ID로 교체하세요.
// 사용자를 위한 유효한 조직 ID는 ID 토큰 클레임 `organizations`에서 찾을 수 있습니다.
logtoClient.getOrganizationToken("organization-id") { logtoException, organizationToken ->
logtoException?.let { println(it) }
organizationToken?.let { println(it) }
}
// 또는
logtoClient.getOrganizationTokenClaims("organization-id") { logtoException, claims ->
logtoException?.let { println(it) }
claims?.let { println(it) }
}
조직 API 리소스
조직 내 API 리소스에 대한 액세스 토큰을 가져오기 위해, getAccessToken 메서드를 API 리소스와 조직 ID를 매개변수로 사용하여 호출할 수 있습니다:
logtoClient.getAccessToken(
'https://shopping.your-app.com/api',
organizationId
) { logtoException, accessToken ->
println("AccessToken:$accessToken")
}