Skip to content

Scope Reference

A scope is a space-separated string (profile email phone, not comma-separated).

Standard scopes

scopeFields returned by userinfoNotes
profile:basicsub, preferred_name, full_nameBasic identity. The profile alias is accepted, but we recommend registering and requesting profile:basic.
emailsub, email, email_verified
phonesub, phone_numberRequires separate consent
addresssub, address, postal_codeRequires separate consent
identity:levelsub, identity_verified_level (0/1/2/3)Identity verification level. New apps must request it explicitly (legacy apps get it automatically with profile).
openidIssues an id_token + subEnables OIDC 1.0

identity_verified_level values: 0 unverified · 1 email_verified · 2 phone_verified · 3 sp_verified. logi does not hold real-name or national ID data — it provides only the integer flag.

Custom scopes

The namespaced form is required: <namespace>:<key> (exactly one colon):

krx_listing:reviewer_role
blog:post.write

These are stored in the User#custom_claims jsonb as {namespace: {key: value}}, and merged into the id_token/userinfo when the scope is requested.

allowed_scopes at app registration

json
{
  "oauth_application": {
    "redirect_uris": ["https://app.example.com/cb"],
    "allowed_scopes": ["profile", "email"]
  }
}

Configuring scopes

bash
logi apps edit <id> --add-scope phone
ruby
app = OauthApplication.kept.find_by(name: "your_app")
app.set_scopes!(["openid", "profile", "email", "phone"])

Scope drift handling

The default policy is block — if even one requested scope is not in allowed_scopes, the entire request is rejected with invalid_scope (a callback-safe redirect). An operator can relax this per app to log_only (silently drop the unregistered scopes and proceed with the registered subset) or alert (log_only plus an admin notification).

CaseDefault (block)log_only / alert
All requested scopes are registered✅ Proceed✅ Proceed
Some are unregisteredinvalid_scope⚠️ Drop the unregistered ones, proceed with the registered ones
All are unregisteredinvalid_scopeinvalid_scope
A required: true scope is missing from the effective setinvalid_scopeinvalid_scope

Regardless of the policy, drift is always recorded (log + drift record + a one-time webhook when a webhook_url is configured) — even when the request is rejected by block, so the operator can trace the cause.

How to detect it

1. Server logs (grep by client_id):

[oauth] scope_drift app_id=4 client_id=logi_xxx policy=block dropped=phone,address kept=profile,email

2. Webhook scope.drift_detected (HMAC-SHA256 signed, fired once per (app_id, scope_name) pair):

json
{
  "event_type": "scope.drift_detected",
  "application_id": 4,
  "payload": {
    "scope_name": "phone",
    "client_id": "logi_xxx",
    "first_seen_at": "2026-04-29T01:30:00Z",
    "allowed_scopes": ["openid", "profile:basic", "email"]
  }
}

3. Token response header X-Logi-Scope-Drift — echoes the drift recorded within the last 7 days on successful token responses. Under the default block policy, a request containing drift is itself rejected with invalid_scope, so this header appears on subsequent successful token responses (drift-free requests) as a history echo:

http
HTTP/1.1 200 OK
X-Logi-Scope-Drift: address,phone
ruby
if drift = res["X-Logi-Scope-Drift"]
  Rails.logger.warn("[logi] scope drift: #{drift}")
end

4. Escalation webhook scope.drift_unresolved — fired once if the drift is still ongoing 7 days after the initial notification.

5. Developer dashboard — a "Scope drift" pin on the Apps card, with a drift table in the detail view.

Marking a scope required

ruby
app.oauth_application_scopes.create!(oauth_scope: email_scope, required: true)

If the user declines a required scope → access_denied.

Re-authorization UX

After a user has consented to profile email:

Requested scopeBehavior
Same or narrower (profile)Skip the UI, issue the code immediately
Expanded (profile email phone)"NEW" badge + additional consent
After consent has been revokedShow the consent screen again

How to request

GET /oauth/authorize?...&scope=profile+email+openid&...

Space-separated; use %20 or + when URL-encoding. The scope field in the response echoes the scopes actually granted (the user may consent to only a subset).

Identity가 제품의 신뢰를 만듭니다.