Quickstart (5분)
관리형 IdP https://1pass.dev 기준. 자체 호스팅은 self-hosting.
사전 준비
bash, curl, openssl, python3.
1. 계정 만들기
https://start.1pass.dev/console/signup → 이메일 + 비밀번호.
앱(RP) 등록 전 이메일 인증 필요
개발자 콘솔에서 앱(RP)을 등록하려면 먼저 이메일 인증이 필요합니다. 가입 후 인증 메일의 링크를 클릭하거나 콘솔 /account 에서 재발송하세요. 미인증 시 등록이 차단됩니다. → 앱 등록 가이드 · 사전 요건
2. OAuth 앱 등록
https://start.1pass.dev/developer/applications/new:
- Redirect URI:
http://localhost:4000/auth/callback - Client type: Confidential (백엔드 보유) / Public (모바일·SPA·CLI 단독)
저장 직후 client_secret 이 한 번만 표시 — 즉시 .env 저장. 분실 시 재발급만 가능.
앱 상세 → Scopes 관리 → openid, profile:basic, email 활성화.
id_token 이 필요하면 openid 필수
신원을 id_token 페이로드에서 직접 읽는 패턴(SPA·서버리스에서 흔함)은 openid scope 가 있어야 합니다. 없으면 token 응답에 id_token 이 빠지고, 신원은 userinfo 로만 조회할 수 있습니다. profile 은 profile:basic 의 alias 지만 등록·요청은 profile:basic 로 통일하세요. — Scope 레퍼런스
Client type 결정: Public vs Confidential.
3. 환경 변수
export LOGI="https://api.1pass.dev"
export CLIENT_ID="logi_..."
export CLIENT_SECRET="..." # Public client 면 생략
export REDIRECT="http://localhost:4000/auth/callback"끝점 발견: $LOGI/.well-known/openid-configuration.
4. PKCE 페어 생성
VERIFIER=$(openssl rand -hex 32)
CHALLENGE=$(printf '%s' "$VERIFIER" \
| openssl dgst -sha256 -binary \
| python3 -c 'import sys,base64; print(base64.urlsafe_b64encode(sys.stdin.buffer.read()).rstrip(b"=").decode())')
echo "verifier = $VERIFIER"
echo "challenge = $CHALLENGE"5. Authorization Code 받기
open "$LOGI/oauth/authorize?\
client_id=$CLIENT_ID&\
redirect_uri=$REDIRECT&\
response_type=code&\
scope=openid+profile:basic+email&\
state=random_xyz&\
code_challenge=$CHALLENGE&\
code_challenge_method=S256"→ http://localhost:4000/auth/callback?code=...&state=random_xyz. code 값 복사.
로그인 화면에 특정 방법만 노출하기
사내에서 Google Workspace 만 쓰는 등 특정 로그인 방법만 보여주고 싶다면 authorize URL 에 provider 파라미터를 추가하세요 (예: &provider=google → "Google로 계속" 버튼만 노출). 콘솔에서 앱 단위로 강제할 수도 있습니다. 자세한 동작은 로그인 방법 제한 참조.
6. Token 교환
CODE="복사한_code"
curl -s -X POST "$LOGI/oauth/token" \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=$REDIRECT \
-d code_verifier=$VERIFIER \
-d client_id=$CLIENT_ID \
-d client_secret=$CLIENT_SECRETPublic client: -d client_secret=... 줄 제거. 보내면 invalid_client 거절.
curl -s -X POST "$LOGI/oauth/token" \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=$REDIRECT \
-d code_verifier=$VERIFIER \
-d client_id=$CLIENT_ID응답:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "DWxB...",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
"scope": "openid profile:basic email"
}id_token 은 openid scope 를 요청했을 때만 포함됩니다.
7. 신원 조회 — 두 가지 방법
7a. id_token 에서 sub 읽기 (서명 재검증 불요)
/oauth/token 응답을 TLS 로 직접 받았다면 id_token 페이로드의 sub 를 바로 신뢰할 수 있습니다. 단 id_token 에는 sub 등 OIDC 표준 claim 만 있고 email·name 은 없습니다.
echo "$ID_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
# → {"iss":"logi","sub":"1","aud":"logi_...","exp":...,"iat":...,"at_hash":"..."}토큰 엔드포인트에서 직접 받았다면 서명 재검증은 선택
같은 백엔드 요청에서 /oauth/token 으로부터 TLS 로 직접 수신한 id_token 은 페이로드를 신뢰해도 됩니다(OIDC §3.1.3.7 #6 예외). 서명(JWKS) 검증은 id_token 이 신뢰할 수 없는 경로(front-channel, 저장 후 재사용, 중계)로 들어올 때만 필수입니다. → JWKS 검증
7b. userinfo 로 프로필(email·name) 받기
email·name 등 프로필은 id_token 에 없으므로 access_token 으로 userinfo 를 호출합니다.
ACCESS_TOKEN="위_응답의_access_token"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "$LOGI/oauth/userinfo"
# → {"sub":"1","nickname":"길동","name":"홍길동","email":"user@example.com","email_verified":true}트러블슈팅
invalid_client: Public client 가client_secret전송 — 줄 제거.- PKCE: S256 필수, plain 거절.
redirect_uri는 https / 127.0.0.1 / localhost / custom-scheme 만 허용.
다음 단계
- 웹: Next.js · SPA + 서버리스 (Vercel) · Rails 8 · Express
- 모바일: iOS · Android · Flutter · React Native
- 버튼 컴포넌트 · Refresh rotation · Webhook · Account Merge · Security