인증
모든 IDCloud API(Web & SDK 및 API 계약)는 OAuth2 JWT Bearer Grant Type (RFC 7523)을 사용합니다. 백엔드에서 단기 JWT 어서션을 생성하고, 이를 Bearer 토큰으로 교환한 후, 이후의 모든 호출에 해당 토큰을 사용합니다.
JWT 어서션은 백엔드에서만 생성해야 합니다. 프론트엔드 코드, 모바일 앱, 저장소 또는 로그에 개인 키를 노출하지 마세요.
자격 증명 획득
토큰을 생성하기 전에, Unico에서 프로비저닝한 서비스 계정이 필요합니다. Unico 지원팀에 문의하여 다음을 제공하세요:
- 서비스 계정 이름(최대 12자)
- 담당자 이름, 이메 일 및 전화번호(브라질, 미국 또는 멕시코 번호만 가능)
수신하게 될 정보:
- 고유 계정 이름
- 테넌트 ID
- 기본 JWT 페이로드
- 개인 키 파일(
.pem형식)
UAT와 프로덕션용 서비스 계정을 별도로 유지하세요.
JWT 어서션 생성
어서션은 컴팩트 JWS 형식의 JWT입니다: {Base64url(Header)}.{Base64url(Payload)}.{Base64url(Signature)}.
{
"alg": "RS256",
"typ": "JWT"
}
| 클레임 | 값 | 비고 |
|---|---|---|
iss | <account_name>@<tenant_id>.iam.acesso.io | 자격 증명과 함께 제공됨 |
aud | https://identityhomolog.acesso.io (UAT) 또는 https://identity.acesso.io (프로덕션) | 토큰 엔드포인트 호스트와 일치해야 함 |
scope | * | 모든 권한 부여 |
iat | Unix 타임스탬프(초) | JWT 발급 시각 |
exp | iat + 최대 3600 | iat로부터 1시간을 초과할 수 없음 |
{
"aud": "https://identity.acesso.io",
"scope": "*",
"iat": 1738086000,
"exp": 1738089600
}
Unico에서 제공한 .pem 개인 키로 RS256(RSA + SHA-256)을 사용하여 헤더와 페이로드에 서명합니다.
위에 나열되지 않은 필드(예: sub, jti, nbf)는 1.2.22 오류를 유발합니다. 표시된 클레임만 사용하세요.
토큰 요청
토큰 엔드포인트는 두 계약 모두 동일합니다:
| 환경 | 엔드포인트 |
|---|---|
| 프로덕션 | POST https://identity.acesso.io/oauth2/token |
| UAT | POST https://identityhomolog.acesso.io/oauth2/token |
| 파라미터 | 값 |
|---|---|
Content-Type | application/x-www-form-urlencoded |
grant_type | urn:ietf:params:oauth:grant-type:jwt-bearer |
assertion | 서명된 JWT |
- cURL
- Node.js
- Python
curl -X POST https://identity.acesso.io/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
-d "assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
import jwt from 'jsonwebtoken';
import fs from 'fs';
import qs from 'querystring';
const privateKey = fs.readFileSync('./private-key.pem');
const now = Math.floor(Date.now() / 1000);
const assertion = jwt.sign(
{
aud: 'https://identity.acesso.io',
scope: '*',
iat: now,
exp: now + 3600,
},
privateKey,
{ algorithm: 'RS256' }
);
const res = await fetch('https://identity.acesso.io/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: qs.stringify({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion,
}),
});
const { access_token, expires_in } = await res.json();
import time
import jwt # PyJWT
import requests
with open("private-key.pem", "rb") as f:
private_key = f.read()
now = int(time.time())
assertion = jwt.encode(
{
"aud": "https://identity.acesso.io",
"scope": "*",
"iat": now,
"exp": now + 3600,
},
private_key,
algorithm="RS256",
)
response = requests.post(
"https://identity.acesso.io/oauth2/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": assertion,
},
)
token_data = response.json()
access_token = token_data["access_token"]
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
| 필드 | 타입 | 설명 |
|---|---|---|
access_token | string | JWT 액세스 토큰. 모든 API 호출의 Authorization: Bearer <token>에 사용합니다. |
expires_in | integer | 초 단위 만료 시간. 예: 3600. |
token_type | string | 항상 Bearer. |
토큰 사용
모든 API 요청의 Authorization 헤더에 토큰을 추가합니다. 헤더는 두 계약 모두 동일하며, 차이점은 호출하는 API의 호스트와 경로입니다:
| 계약 | 프로덕션 호스트 | UAT 호스트 |
|---|---|---|
| Web & SDK | https://api.idcloud.unico.app | https://api.idcloud.uat.unico.app |
| API | https://api.id.unico.app | https://api.id.uat.unico.app |
Web & SDK — Authorization만 필요한 인증 헤더입니다:
curl -X POST https://api.idcloud.unico.app/client/v1/process \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... }'
API — Authorization는 APIKEY 헤더와 함께 사용됩니다:
curl -X POST https://api.id.unico.app/processes/v1 \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "APIKEY: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ ... }'
토큰 갱신
토큰은 3600초 후에 만료됩니다. 백엔드에서 사전 갱신을 구현하세요:
- 토큰 응답의
expires_in을 추적하고 만료 타임스탬프를 저장합니다. - 만료 10분 이하가 남았을 때 새 토큰을 요청합니다.
- 갱신을 트리거하기 위해 프로덕션에서
401을 기다리지 마세요.
API 레퍼런스
토큰 엔드포인트의 전체 OpenAPI 스펙은 authentication.yaml에서 확인할 수 있습니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
POST | /oauth2/token | 서명된 JWT 어서션을 Bearer 액세스 토큰으로 교환 |
오류 코드
| 코드 | 설명 | 조치 |
|---|---|---|
1.0.1 | iss 형성에 제공된 ID가 올바르지 않음 | iss 필드가 개인 키 생성 시 제공된 테넌트 ID와 일치하는지 확인 |
1.0.14 | 애플리케이션이 활성 상태가 아님 | 프로젝트 관리자에게 사용 중인 애플리케이션이 활성화되어 있는지 확인 |
1.1.1 | scope 파라미터가 제공되지 않음 | JWT 페이로드에 "scope": "*" 추가 |
1.2.4 | JWT 어서션이 유효하지 않음 | JWT 어서션이 더 이상 유효하지 않습니다. 두 가지 원인: (a) 현재 시각이 exp를 초과한 경우(JWT가 실제로 만료됨 — 각 토큰 요청마다 새 어서션을 생성하세요); 또는 (b) exp가 iat + 3600을 초과한 경우(유효 기간이 너무 긴 경우 — exp를 iat + 3600으로 제한하세요). |
1.2.5 | JWT 유효성 검사 실패 | JWT를 검증할 수 없습니다. 파라미터를 확인하고 RS256과 올바른 개인 키로 서명되었는지 확인 |
1.2.6 | 개인 키가 더 이상 유효하지 않음 | JWT 서명에 사용된 개인 키가 더 이상 허용되지 않습니다. 계정에 대한 새 자격 증명 요청 |
1.2.7 | JWT가 이미 사용됨 | JWT가 이미 사용되어 더 이상 허용되지 않습니다. 각 토큰 요청에 새 어서션 생성 |
1.2.11 | 계정이 활성 상태가 아님 | 사용 중인 계정이 활성화되어 있지 않음 |
1.2.14 | 계정에 필요한 권한이 없음 | 사용 중인 계정에 필요한 권한이 없음 |
1.2.18 | 계정이 일시적으로 잠김 | 잘못된 인증 시도 횟수 초과로 계정이 일시적으로 잠겼음 |
1.2.19 | 승인되지 않은 사용자 가장 | JWT에 가장이 승인되지 않은 계정을 가리키는 sub 클레임이 포함되어 있습니다. 페이로드에서 sub 클레임을 제거하세요. |
1.2.20 | JWT 디코딩 실패 | JWT 디코딩에 실패했습니다. 토큰 형식과 RS256으로 서명되었는지 확인하세요. |
1.2.21 | 잘못된 개인 키 / 인증 실패 | 이 계정의 알려진 키로 JWT 서명을 검증할 수 없습니다. 해당 서비스 계정 및 환경에 올바른 .pem 개인 키를 사용하고 있는지 확인하세요. |
1.2.22 | 페이로드에 허용되지 않는 필드 | JWT에 허용되지 않는 추가 페이로드 필드가 포함되어 있습니다. 이 가이드에 나열되지 않은 클레임을 제거하세요(예: sub, jti, nbf). 참고: sub 클레임을 포함하고 1.2.19를 받은 경우 해당 오류가 우선합니다. |
1.3.1 | IP 접근 제한 | IP가 이 계정의 허용 목록에 없음 |
1.3.2 | 시간 기반 접근 제한 | 요청이 이 계정의 허용된 시간 범위 외에 있음 |