본문으로 건너뛰기
문서 · 모든 OIDC 라이브러리

빠른 시작: 모든 OIDC 라이브러리

Usher는 표준 OpenID Connect 공급자입니다. 사용하는 언어에 인증받은 OIDC 클라이언트가 있다면 필요한 것은 이미 다 갖춘 셈입니다. 클라이언트가 환경의 디스커버리 문서를 가리키게 하고, PKCE를 적용한 인가 코드 흐름을 사용하세요.

설정

설정값
발급자https://<handle>.usherid.com
디스커버리 URLhttps://<handle>.usherid.com/.well-known/openid-configuration
흐름인가 코드(response_type=code)
PKCE필수, code_challenge_method=S256
범위openid profile email offline_access
클라이언트 인증token_endpoint_auth_methods_supported에 나열된 방식

환경의 핸들은 Usher Console에서 발급자 옆에 표시됩니다. 개발 환경에는 wise-otter-42처럼 무작위 핸들이 주어지고, 프로덕션 환경은 직접 정한 핸들을 사용합니다. 프로덕션 환경은 login.acme.com처럼 자체 도메인을 사용할 수도 있습니다. CNAME 레코드로 도메인이 Usher를 가리키도록 설정하면, 그 도메인이 해당 환경의 발급자가 됩니다.

디스커버리 문서 읽기

클라이언트에 필요한 모든 엔드포인트와 키가 여기에 나열되어 있습니다. URL을 코드에 복사해 넣지 말고, 라이브러리가 이 문서를 읽게 하세요.

터미널
curl -s https://acme.usherid.com/.well-known/openid-configuration | jq
응답(일부)
{
  "issuer": "https://acme.usherid.com",
  "authorization_endpoint": "https://acme.usherid.com/api/auth/oauth2/authorize",
  "token_endpoint": "https://acme.usherid.com/api/auth/oauth2/token",
  "userinfo_endpoint": "https://acme.usherid.com/api/auth/oauth2/userinfo",
  "jwks_uri": "https://acme.usherid.com/api/auth/jwks",
  "introspection_endpoint": "https://acme.usherid.com/api/auth/oauth2/introspect",
  "revocation_endpoint": "https://acme.usherid.com/api/auth/oauth2/revoke",
  "end_session_endpoint": "https://acme.usherid.com/api/auth/oauth2/end-session",
  "scopes_supported": ["openid", "profile", "email", "offline_access"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
  "code_challenge_methods_supported": ["S256"]
}

흐름

사용자를 인가 엔드포인트로 보내기

무작위 PKCE 검증자를 만들고, 무작위 state와 함께 이 브라우저의 세션만 접근할 수 있는 곳에 보관하세요(유효 기간이 짧은 httpOnly 쿠키가 적합합니다). 그런 다음 response_type=code, client_id와 redirect_uri, 위의 범위, state, 그리고 검증자로 만든 S256 code_challenge를 담아 리디렉션합니다.

로그인은 Lobby에 맡기기

여기서부터는 Lobby가 처리합니다. 앱의 이름을 보여 주고, 환경에서 허용한 로그인 방법을 제공하며, 처음 한 번은 동의를 요청합니다.

콜백 확인하기

Usher는 code와 state를 담아 redirect_uri로 리디렉션합니다. state가 보관해 둔 값과 일치하지 않으면 응답을 거부하세요. 코드는 일회용이며 5분 동안 유효합니다.

코드를 토큰으로 교환하기

서버에서 코드와 PKCE 검증자를 토큰 엔드포인트로 POST 요청합니다.

토큰 요청
curl -s -X POST https://acme.usherid.com/api/auth/oauth2/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://app.acme.example/callback \
-d code_verifier="$CODE_VERIFIER"

ID 토큰 검증하기

jwks_uri의 키로 서명을 검증하고, iss가 발급자와 같은지, aud가 클라이언트 ID와 같은지, exp가 아직 지나지 않았는지 확인하세요. 라이브러리가 이 작업을 대신 처리하니, 이 기능이 꺼져 있지 않은지만 확인하면 됩니다.

Node.js에서

openid-client는 검증을 포함해 위의 모든 작업을 처리합니다.

openid-client
import * as client from 'openid-client'

// discovery: every endpoint and key comes from the issuer
const config = await client.discovery(
  new URL('https://acme.usherid.com'),
  process.env.USHER_CLIENT_ID!,
  process.env.USHER_CLIENT_SECRET!,
)

// 1. send them to the Lobby, with PKCE (S256) and a state to check later
const codeVerifier = client.randomPKCECodeVerifier()
const state = client.randomState()
const url = client.buildAuthorizationUrl(config, {
  redirect_uri: 'https://app.acme.example/callback',
  scope: 'openid profile email offline_access',
  code_challenge: await client.calculatePKCECodeChallenge(codeVerifier),
  code_challenge_method: 'S256',
  state,
})
// keep codeVerifier and state in a short-lived httpOnly cookie, then redirect to url

// 2. back at /callback: swap the code for tokens
const tokens = await client.authorizationCodeGrant(config, new URL(request.url), {
  pkceCodeVerifier: codeVerifier,
  expectedState: state,
})
const { sub, email, name } = tokens.claims()!

다른 언어에서

널리 쓰이는 다음 라이브러리는 모두 표준 OpenID Connect를 지원하며, Usher 전용 설정 없이 Usher와 함께 작동합니다.

언어라이브러리
PythonAuthlib
Gogolang.org/x/oauth2와 함께 사용하는 coreos/go-oidc
JavaSpring Security OAuth 2.0 Client
.NETMicrosoft.AspNetCore.Authentication.OpenIdConnect
Rubyomniauth_openid_connect
PHPjumbojett/openid-connect-php

로그인 상태 유지하기

offline_access 범위를 요청하면 토큰 응답에 리프레시 토큰이 포함됩니다. 기존 액세스 토큰이 만료되면 grant_type=refresh_token과 함께 리프레시 토큰을 사용해 새 액세스 토큰을 받으세요. 리프레시 토큰은 교체됩니다. 사용할 때마다 새 토큰이 반환되고, 이전 토큰은 더 이상 작동하지 않습니다.

로그아웃하기

먼저 앱의 세션을 지운 다음, post_logout_redirect_uri(가지고 있다면 id_token_hint도 함께)를 담아 end_session_endpoint로 리디렉션하세요. 그러면 Lobby에서도 로그아웃됩니다.