본문으로 건너뛰기

Device flow: Logto로 인증 (Authentication)하기

노트:

이 가이드는 Logto 콘솔에서 "네이티브" 유형의 애플리케이션을 생성하고, 인가 플로우로 device flow를 선택했다고 가정합니다.

소개 (Introduction)​

OAuth 2.0 디바이스 인가 그랜트 (device flow)는 스마트 TV, 게임 콘솔, CLI 도구, IoT 기기 등 입력 기능이 제한된 디바이스를 위해 설계되었습니다. 사용자는 디바이스에서 로그인 과정을 시작하지만, 브라우저가 있는 별도의 기기(예: 휴대폰, 노트북)에서 인증 (Authentication)을 완료할 수 있습니다.

디바이스 자체에서 브라우저 기반 로그인 플로우를 처리할 수 없으므로, 디바이스는 짧은 코드와 URL을 표시합니다. 사용자는 다른 기기에서 해당 URL에 접속해 코드를 입력하고 로그인합니다. 그동안 원래의 디바이스는 인가가 완료될 때까지 Logto에 폴링합니다.

애플리케이션 자격 증명 얻기​

Logto 콘솔에서 애플리케이션 상세 페이지로 이동하여 다음 자격 증명을 확인하세요:

  • App ID: 애플리케이션의 고유 식별자 (client_id라고도 함).
  • Logto endpoint: Logto 인가 서버 엔드포인트. Logto 콘솔의 "Application details"에서 확인할 수 있습니다.

Logto Cloud의 경우 엔드포인트는 https://{your-tenant-id}.logto.app 입니다.

노트:

Device flow 앱은 공개 클라이언트이므로 App Secret이 필요하지 않습니다.

디바이스 코드 요청하기​

디바이스 인가 플로우를 시작하려면 device authorization 엔드포인트에 POST 요청을 보냅니다:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile'

응답에는 다음이 포함됩니다:

필드설명
device_code토큰 엔드포인트 폴링 시 앱에서 사용할 고유 코드입니다.
user_code사용자가 브라우저에 입력할 짧은 코드입니다.
verification_uri사용자가 user_code를 입력할 URL입니다.
verification_uri_completeuser_code가 미리 입력된 URL입니다. 사용자는 이 URL에 바로 접속해 수동 코드 입력을 건너뛸 수 있습니다. QR 코드, 클릭 가능한 링크 등으로 제공할 수 있습니다.
expires_indevice_code 및 user_code의 유효 시간(초)입니다. 만료 후에는 폴링을 중단해야 합니다.

사용자에게 인증 URL 표시하기​

디바이스 화면에 user_code와 verification_uri를 표시하세요.

또는 코드가 미리 입력된 verification_uri_complete를 사용할 수도 있습니다. 사용자는 확인만 하면 됩니다. QR 코드, 클릭 가능한 링크 등 원하는 방식으로 제공할 수 있습니다.

토큰 폴링하기​

사용자가 브라우저에서 인증 (Authentication)을 완료하는 동안, 디바이스는 토큰 엔드포인트를 폴링해야 합니다. 앱은 폴링 요청 사이에 최소 5초 이상 대기해야 합니다:

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
--data-urlencode 'device_code=DEVICE_CODE'

DEVICE_CODE를 device authorization 응답에서 받은 device_code 값으로 교체하세요.

폴링을 중단해야 하는 경우:

  • 토큰 응답을 성공적으로 받은 경우
  • device code 응답의 expires_in 시간이 경과한 경우
  • expired_token 또는 access_denied와 같은 재시도 불가 오류를 받은 경우

토큰 응답​

사용자가 승인을 완료하면 응답에는 다음이 포함됩니다:

필드설명
access_token액세스 토큰 (Access token). 기본적으로 불투명 토큰 (Opaque token)이며, resource를 요청하면 해당 리소스 URI가 aud로 설정된 JWT입니다.
id_token사용자 아이덴티티 클레임 (Claim)이 포함된 ID 토큰 (ID token). openid 스코프를 요청한 경우에만 제공됩니다.
refresh_token재인증 없이 새 토큰을 얻을 때 사용합니다. offline_access 스코프를 요청한 경우에만 제공됩니다.
token_type항상 Bearer입니다.
expires_in토큰의 유효 시간(초)입니다.
scope인가 서버가 부여한 스코프 (Scope)입니다.

체크포인트: 디바이스 플로우 테스트하기​

이제 디바이스 플로우 연동을 테스트하세요:

  1. 앱을 실행하고 device flow를 트리거하여 device_code와 user_code를 받으세요.
  2. 브라우저에서 verification_uri를 열고 user_code를 입력하거나, verification_uri_complete로 수동 입력을 건너뛰세요.
  3. 브라우저에서 로그인 과정을 완료하세요.
  4. 앱이 폴링 후 토큰을 정상적으로 받는지 확인하세요.

사용자 정보 얻기​

ID 토큰 클레임 (Claim) 디코딩하기​

토큰 응답에서 반환된 id_token은 표준 JSON Web Token (JWT)입니다. Base64URL로 인코딩된 페이로드(마침표로 구분된 두 번째 부분)를 디코딩하면 추가 네트워크 요청 없이 기본 사용자 클레임 (Claim)에 접근할 수 있습니다.

디코딩된 페이로드에는 요청한 스코프 (Scope)에 따라 sub(사용자 ID), name, email 등 클레임 (Claim)이 포함됩니다.

팁:

운영 환경에서는 JWT 서명을 검증한 후 클레임 (Claim)을 신뢰해야 합니다. Logto 엔드포인트의 JWKS (https://your.logto.endpoint/oidc/jwks)를 사용해 토큰을 검증하세요.

userinfo 엔드포인트에서 가져오기​

ID 토큰에는 요청한 스코프 (Scope)에 따라 기본 클레임 (Claim)만 포함됩니다. 일부 확장 클레임 (예: custom_data, identities)은 OIDC UserInfo 엔드포인트에서만 확인할 수 있습니다:

curl --request GET 'https://your.logto.endpoint/oidc/me' \
--header 'Authorization: Bearer ACCESS_TOKEN'

ACCESS_TOKEN을 토큰 응답에서 받은 불투명 액세스 토큰 (Opaque token) (JWT 리소스 토큰이 아님)으로 교체하세요. 응답은 부여된 스코프 (Scope)에 따라 사용자의 클레임 (Claim)이 포함된 JSON 객체입니다.

추가 클레임 (Claim) 요청하기​

ID 토큰에 일부 사용자 정보가 누락된 경우가 있습니다. 이는 OAuth 2.0 및 OpenID Connect (OIDC)가 최소 권한 원칙(PoLP)을 따르도록 설계되었고, Logto도 이를 기반으로 하기 때문입니다.

기본적으로 제한된 클레임 (Claim)만 반환됩니다. 더 많은 정보를 원하시면, 추가적인 스코프 (Scope)를 요청하여 더 많은 클레임에 접근할 수 있습니다.

정보:

"클레임 (Claim)"은 주체에 대해 주장하는 내용이며, "스코프 (Scope)"는 클레임의 그룹입니다. 현재의 경우, 클레임은 사용자에 대한 정보입니다.

다음은 스코프 - 클레임 관계의 비규범적 예시입니다:

팁:

"sub" 클레임은 "주체"를 의미하며, 이는 사용자의 고유 식별자 (즉, 사용자 ID)입니다.

Logto SDK는 항상 세 가지 스코프를 요청합니다: openid, profile, 그리고 offline_access.

추가 스코프 (Scope)를 요청하려면 device authorization 요청의 scope 파라미터에 포함하세요. 예를 들어, 사용자의 이메일과 전화번호를 요청하려면:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile email phone'

스코프 (Scope)와 클레임 (Claim)​

지원되는 스코프와 해당 클레임(Claim)의 목록은 다음과 같습니다:

표준 OIDC 스코프​

openid (기본값)

클레임(Claim) 이름타입설명
substring사용자의 고유 식별자

profile (기본값)

클레임(Claim) 이름타입설명
namestring사용자의 전체 이름
usernamestring사용자의 사용자명
picturestring최종 사용자의 프로필 사진 URL. 이 URL은 이미지 파일(예: PNG, JPEG, GIF 이미지 파일)을 가리켜야 하며, 이미지를 포함한 웹 페이지가 아니어야 합니다. 이 URL은 최종 사용자를 설명할 때 표시하기에 적합한 프로필 사진을 명확히 참조해야 하며, 최종 사용자가 임의로 촬영한 사진이 아니어야 합니다.
created_atnumber최종 사용자가 생성된 시간. 시간은 Unix epoch (1970-01-01T00:00:00Z) 이후 밀리초로 표시됩니다.
updated_atnumber최종 사용자의 정보가 마지막으로 업데이트된 시간. 시간은 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) 이름타입설명
emailstring사용자의 이메일 주소
email_verifiedboolean이메일 주소가 인증되었는지 여부

phone

클레임(Claim) 이름타입설명
phone_numberstring사용자의 전화번호
phone_number_verifiedboolean전화번호가 인증되었는지 여부

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_dataobject사용자의 커스텀 데이터

identities

클레임(Claim) 이름타입설명기본적으로 ID 토큰에 포함됨
identitiesobject사용자의 연결된 아이덴티티
sso_identitiesarray사용자의 연결된 SSO 아이덴티티

roles

클레임(Claim) 이름타입설명기본적으로 ID 토큰에 포함됨
rolesstring[]사용자의 역할 (Role)✅

urn:logto:scope:organizations

클레임(Claim) 이름타입설명기본적으로 ID 토큰에 포함됨
organizationsstring[]사용자가 속한 조직 (Organization) ID✅
organization_dataobject[]사용자가 속한 조직 (Organization) 데이터
노트:

이러한 조직 (Organization) 클레임(Claim)은 불투명 토큰 (Opaque token)을 사용할 때도 userinfo 엔드포인트를 통해 조회할 수 있습니다. 그러나 불투명 토큰 (Opaque token)은 조직 토큰 (Organization token)으로 사용되어 조직별 리소스에 접근할 수 없습니다. 자세한 내용은 불투명 토큰 (Opaque token)과 조직 (Organization)을 참고하세요.

urn:logto:scope:organization_roles

클레임(Claim) 이름타입설명기본적으로 ID 토큰에 포함됨
organization_rolesstring[]사용자가 속한 조직 (Organization)의 역할 (Role), 형식: <organization_id>:<role_name>✅

API 리소스와 조직​

먼저 🔐 역할 기반 접근 제어 (RBAC)를 읽어 Logto RBAC의 기본 개념과 API 리소스를 적절히 설정하는 방법을 이해하는 것을 권장합니다.

API 리소스 접근 요청하기​

특정 API 리소스에 접근하려면 device authorization 요청에 resource 파라미터를 포함하세요:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access' \
--data-urlencode 'resource=https://your-api-resource-indicator'

사용자가 인가 (Authorization)를 완료하고 refresh token을 받으면, 해당 API 리소스용 JWT 액세스 토큰 (Access token)을 가져올 수 있습니다:

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'resource=https://your-api-resource-indicator'

응답에는 API 리소스 인디케이터가 aud로 설정된 JWT access_token이 포함됩니다.

노트:

refresh_token은 최초 device authorization 요청에 offline_access 스코프 (Scope)가 포함된 경우에만 제공됩니다. Logto는 토큰 로테이션을 사용하므로 항상 최신 refresh_token을 저장하고 사용하세요.

조직 토큰 (Organization token) 가져오기​

조직이 처음이라면, 🏢 조직 (다중 테넌시)를 먼저 읽어보세요.

조직 관련 정보를 요청하려면 device authorization 요청에 urn:logto:scope:organizations 스코프 (Scope)를 추가하세요:

curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access urn:logto:scope:organizations' \
--data-urlencode 'resource=urn:logto:resource:organizations'

사용자가 로그인하면 refresh token을 사용해 조직 토큰 (Organization token)을 가져올 수 있습니다:

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id'

응답에는 지정한 조직에 범위가 지정된 액세스 토큰 (Access token)이 포함됩니다.

조직 API 리소스​

조직 내 API 리소스용 액세스 토큰을 가져오려면 resource와 organization_id 파라미터를 모두 포함하세요:

curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id' \
--data-urlencode 'resource=https://your-api-resource-indicator'

추가 자료​

엔드유저 플로우: 인증 (Authentication) 플로우, 계정 플로우, 조직 플로우 커넥터 구성하기 인가 (Authorization)