Verify API

PASS식 1회성 SSAFY 인증

외부 앱의 로그인은 유지하면서 이번 거래에서 사용자가 SSAFY 구성원인지 확인합니다. 매 거래마다 Mattermost DM 코드 인증을 요구합니다.

Canonical issuer

Verify API의 issuer는 문서를 연 도메인이 아니라 아래 canonical 값으로 고정합니다.

Canonical issuer?
https://verify.myknow.xyz
Canonical API base URL?
https://verify.myknow.xyz
Canonical SDK URL?
https://verify.myknow.xyz/sdk/ssafy-verify.js
Compatibility alias?
none
Exact match rule?
callback.iss === "https://verify.myknow.xyz"

Flow summary

Verify API는 외부 앱 로그인과 별개로 이번 거래의 SSAFY 인증 여부만 확인합니다.

1. authorize

외부 앱이 state와 PKCE challenge를 만들고 사용자를 /verify/authorize로 보냄

2. consent

SSAFY Verify가 앱 이름, redirect domain, 제공 정보를 보여줌

3. DM code

사용자가 Mattermost DM으로 받은 6자리 코드를 입력

4. callback

외부 앱 redirect_uri로 code, state, iss 전달

5. token

외부 앱 서버가 /verify/token에서 verification_token 교환

6. verify

JWT 서명과 iss, aud, exp, sub, client_id, verified, auth_time, amr, acr 확인 후 최소 claim 저장

v1 공식 범위

SSAFY Verify v1은 본인인증만 공식 지원합니다. 외부 앱 로그인을 SSAFY Verify로 대체하는 OIDC 연동은 v2에서 지원 예정입니다.

Authorize

사용자를 SSAFY Verify 인증 화면으로 이동시키는 endpoint입니다.

Endpoint
GET /verify/authorize
Success callback
redirect_uri?code=...&state=...&iss=...
Failure callback
redirect_uri?error=...&error_code=...&request_id=...&state=...

Query parameters

client_id?
필수. 승인된 Verify client id
redirect_uri?
필수. client에 등록된 redirect URI와 exact match
scope?
필수. ssafy.verify 포함. 예: ssafy.verify ssafy.affiliation ssafy.name
state?
필수. 외부 앱이 생성하고 callback에서 비교하는 난수
code_challenge?
필수. PKCE S256 challenge
code_challenge_method?
필수. S256만 허용
nonce?
선택. 거래 추적용 난수
authorize-url.txt브라우저 이동 URL 예시
https://verify.myknow.xyz/verify/authorize?
  client_id=client_example_public
  &redirect_uri=https%3A%2F%2Fpartner.example.com%2Fssafy%2Fcallback
  &scope=ssafy.verify%20ssafy.affiliation%20ssafy.name
  &state=random_state_from_partner
  &code_challenge=pkce_challenge
  &code_challenge_method=S256

Callback

사용자가 인증을 완료하면 SSAFY Verify가 등록된 redirect_uri로 이동합니다.

Success fields
code, state, iss
Failure fields
error, error_code, request_id, state
state
외부 앱이 authorize 시작 시 만든 값과 반드시 비교
iss
https://verify.myknow.xyz 와 정확히 일치해야 token exchange 진행
code
브라우저에 저장하지 않고 즉시 서버 route로 전달

Callback fields

code?
성공 시 전달되는 1회성 callback code
state?
authorize 시작 시 외부 앱이 만든 값
iss?
SSAFY Verify issuer
error?
실패 시 OAuth 스타일 error
error_code?
SSAFY Verify의 안정적인 내부 error code
request_id?
실패 추적용 request id
callback-url.txt성공 callback 예시
https://partner.example.com/ssafy/callback?
  code=code_from_ssafy_verify
  &state=random_state_from_partner
  &iss=https%3A%2F%2Fverify.myknow.xyz

Token

callback code를 verification_token JWT로 교환합니다. public client는 client_secret을 보내지 않습니다.

Endpoint
POST /verify/token
Content-Type
application/x-www-form-urlencoded
Cache
no-store 응답

Body parameters

grant_type?
필수. verification_code
code?
필수. callback query의 code
client_id?
필수. 승인된 client id
client_secret?
confidential client에서만 서버에서 전송
code_verifier?
필수. authorize 시작 시 만든 PKCE verifier
curl/verify/token 교환 요청
curl -X POST https://verify.myknow.xyz/verify/token \
  -H "content-type: application/x-www-form-urlencoded" \
  -d "grant_type=verification_code" \
  -d "client_id=client_example_public" \
  -d "code=code_from_callback" \
  -d "code_verifier=pkce_verifier_from_browser"
success.json성공 응답
{
  "verification_token": "jwt_placeholder",
  "token_type": "Bearer",
  "expires_in": 300,
  "scope": "ssafy.verify ssafy.affiliation ssafy.name",
  "result": {
    "verification_id": "verification_id_placeholder",
    "verified": true,
    "sub": "pairwise_subject_placeholder",
    "auth_time": "2026-06-18T00:00:00.000Z"
  }
}

Success response fields

verification_token?
서명된 Verify JWT
token_type?
Bearer
expires_in?
초 단위 만료 시간
scope?
승인되어 반영된 scope 문자열
result?
검증 결과 요약 객체

auth_time 타입

`/verify/token` JSON의 `result.auth_time`은 ISO 8601 문자열입니다. `verification_token` JWT claim의 `auth_time`은 JWT 표준 NumericDate seconds입니다. 파트너 서버가 앱에 내려주는 `authTime`은 JWT claim에서 읽은 seconds 값으로 통일하는 것을 권장합니다.
error.json실패 응답
{
  "ok": false,
  "error": {
    "code": "PKCE_VERIFICATION_FAILED",
    "message": "PKCE 검증에 실패했습니다.",
    "request_id": "req_placeholder"
  }
}

Scope별 반환 claim

scope는 최소한으로 요청합니다. 이름과 프로필 이미지는 별도 scope입니다.

ScopeRequiredClaimsNullable사용 조건
ssafy.verifyyesverified, sub, auth_time, verification_id, amr, acrno모든 Verify 연동에 필요합니다. 외부 앱은 최소 인증 결과만 저장하세요.
ssafy.affiliationnossafy_cohort, ssafy_campus, ssafy_regionyes기수, 캠퍼스, 지역 기준으로 혜택이나 권한을 나눌 때만 요청합니다.
ssafy.namenonameyes사용자 이름 표시나 기존 회원 정보 매칭이 필요할 때만 요청합니다.
ssafy.profile_imagenopictureyes프로필 이미지를 화면에 표시할 때만 요청합니다.
ssafy.rolenossafy_role, ssafy_role_nameyes교육생/운영진 등 역할 기반 기능이 있을 때만 요청합니다.
ssafy.mattermost_idnossafy_mattermost_user_idyes기존 Mattermost 인증 프로젝트의 계정 매핑이 필요할 때만 요청합니다.

Verification token claims

외부 앱 서버는 verification_token의 서명과 claim을 검증한 뒤 세션이나 DB에 최소 결과만 저장합니다.

iss?
SSAFY Verify issuer
aud?
client_id
sub?
client별 pairwise subject
exp?
만료 시각. Verify token은 5분 이하
verification_id?
이번 Verify 거래 id
verified?
항상 true
auth_time?
JWT NumericDate seconds
amr?
mattermost_dm
acr?
urn:ssafy:verify:assurance:mattermost-team-dm:v1
ssafy_*?
승인된 scope에 따라 제공되는 SSAFY claim
ssafy_mattermost_user_id?
Mattermost 고유 user id

Error JSON 경계

SSAFY Verify 원본 오류와 파트너 앱이 자기 frontend에 내려주는 응답 형식을 구분하세요.

SSAFY Verify original?
ok: false, error.code, error.message, error.request_id
Partner app response?
ok: false, errorCode, requestId

Security notes

  • callback code와 verification_token은 로그에 남기지 않습니다.
  • redirect URI는 등록값과 exact match여야 합니다.
  • callback iss가 SSAFY Verify issuer와 다르면 token exchange를 시작하지 않습니다.
  • PKCE verifier는 token exchange 직후 폐기합니다.
  • 이름은 ssafy.name scope가 승인된 경우에만 사용합니다.
  • 프로필 이미지는 ssafy.profile_image scope가 승인된 경우에만 사용합니다.
  • JWT는 iss, aud, exp, sub, client_id, verified, auth_time, amr, acr를 모두 검증합니다.
  • 기존 Mattermost 인증 계정 매핑이 필요할 때만 ssafy.mattermost_id를 요청합니다.
  • Mattermost user id는 raw user object의 id이며, username은 변경 가능한 보조 표시값으로만 취급합니다.
  • cohort는 raw user object가 아니라 수집한 Mattermost team context 기준으로 판단합니다.