Backend Guide
Backend token exchange 공통 구현
모든 웹/모바일 앱은 자기 backend에서 /verify/token 교환과 verification_token 검증을 수행합니다.
이 문서가 맞는 경우
플랫폼별 권장 흐름을 먼저 고정한 뒤, code 교환은 모두 앱 서버에서 처리합니다.
Best for- Next.js Route Handler, Nuxt server route, Express, Spring, Django, Rails 등
Strategy- Client callback code + PKCE verifier -> backend -> SSAFY Verify /verify/token
Callback- client는 code, codeVerifier, redirectUri, iss를 자기 backend로 보내고 backend가 issuer와 JWT를 검증합니다.
Support- 공식 지원: protocol, hosted page, backend exchange contract
준비물
아래 값은 모든 외부 앱 backend가 token exchange 전에 검증해야 하는 입력입니다.
issuer
모든 플랫폼에서 canonical issuer/API base URL은 https://verify.myknow.xyz 입니다
client_id
Developer Portal에서 승인된 public 또는 confidential client id
redirect_uri
client 설정에 exact match로 등록된 callback URL 또는 deep link
scope
ssafy.verify는 필수. 기수/캠퍼스/지역은 ssafy.affiliation, 이름은 ssafy.name, 이미지는 ssafy.profile_image 추가
Mattermost id
기존 Mattermost 인증 프로젝트의 계정 매핑이 필요한 경우에만 ssafy.mattermost_id 추가
backend endpoint
앱 서버에서 code와 code_verifier를 받아 /verify/token으로 교환
client secret 금지
적용 흐름
client request
callback state와 iss를 확인한 뒤 code와 codeVerifier를 backend로 전송
input validation
backend가 code, codeVerifier, redirectUri, iss를 검증
token exchange
backend가 /verify/token에 grant_type=verification_code로 교환
JWT validation
JWKS로 verification_token 서명과 iss, aud, exp, sub, client_id, verified, auth_time, amr, acr를 검증
session save
최소 claim만 앱 session 또는 DB에 저장
client to backend 요청 계약
브라우저와 모바일 앱이 자기 backend로 보내는 최소 JSON입니다. client_secret은 포함하지 않습니다.
붙일 위치
외부 앱의 /api/ssafy/verify-token 같은 endpoint
확인 방법
iss가 SSAFY Verify issuer와 다르면 즉시 거절
client-to-backend.schema.jsonclient callback 결과를 backend로 전달하는 계약{
"method": "POST",
"endpoint": "/api/ssafy/verify-token",
"contentType": "application/json",
"body": {
"code": "code_from_callback",
"codeVerifier": "pkce_verifier_from_client",
"redirectUri": "https://partner.example.com/ssafy",
"iss": "https://verify.myknow.xyz"
},
"validation": {
"code": "required string",
"codeVerifier": "required string",
"redirectUri": "must be one of registered callback URLs",
"iss": "must equal https://verify.myknow.xyz"
}
}SSAFY Verify token exchange
backend가 SSAFY Verify와 직접 통신합니다. confidential client secret은 이 단계에서만 서버 환경변수로 사용합니다.
붙일 위치
외부 앱 backend service
확인 방법
grant_type은 verification_code이고 response는 no-store로 처리
backend-to-ssafy-verify.txt/verify/token form body 계약POST https://verify.myknow.xyz/verify/token
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-store
grant_type=verification_code
client_id=client_example_public
code=code_from_callback
code_verifier=pkce_verifier_from_client
optional confidential client field:
client_secret is read from server env onlyverification_token 검증
token payload를 믿기 전에 JWKS 서명과 필수 claim을 모두 검증합니다.
붙일 위치
외부 앱 backend token validation module
확인 방법
issuer, audience, expiration, subject, client_id, verified, auth_time, amr, acr가 모두 검증됨
verification-token-validation.ymlJWT 검증 필수 조건verification_token validation:
jwks: https://verify.myknow.xyz/verify/jwks
required:
iss: https://verify.myknow.xyz
aud: client_example_public
exp: must be in the future
sub: required pairwise subject
client_id: client_example_public
verified: true
auth_time: NumericDate seconds
amr: contains mattermost_dm
acr: urn:ssafy:verify:assurance:mattermost-team-dm:v1
reject_if:
signature invalid
token expired
issuer mismatch
audience mismatch
verified is not true실패 응답 매핑
SSAFY Verify의 error_code와 request_id를 보존하되 token/code/secret 원문은 public response에 포함하지 않습니다.
붙일 위치
외부 앱 backend error mapper
확인 방법
사용자 화면에는 안전한 message와 request_id만 표시
backend-error-mapping.jsonpartner app public error response 예시{
"ssafyVerifyError": {
"ok": false,
"error": {
"code": "PKCE_VERIFICATION_FAILED",
"message": "PKCE 검증에 실패했습니다.",
"request_id": "req_placeholder"
}
},
"partnerAppResponse": {
"ok": false,
"errorCode": "PKCE_VERIFICATION_FAILED",
"requestId": "req_placeholder",
"message": "인증 요청을 다시 시작해주세요."
}
}공통 서버 token exchange 계약
플랫폼이 무엇이든 최종 교환과 verification_token 검증은 앱 서버에서 수행합니다.
Client request- 앱 또는 브라우저가 자기 backend로 보내는 JSON
Backend action- backend가 /verify/token에 grant_type=verification_code로 교환
Backend response- 앱 세션에 저장해도 되는 최소 인증 결과만 반환
client-to-backend.json앱이 자기 서버에 보내는 body{
"code": "code_from_callback",
"codeVerifier": "pkce_verifier_from_client",
"redirectUri": "https://partner.example.com/ssafy",
"iss": "https://verify.myknow.xyz"
}Client request fields
code?- callback으로 받은 1회성 Verify code
codeVerifier?- PKCE 검증에 필요한 원본 verifier
redirectUri?- 외부 앱에 등록된 callback URL
iss?- callback 응답의 issuer
backend-success.json앱 서버가 클라이언트에 돌려주는 최소 결과{
"ok": true,
"verified": true,
"sub": "pairwise_subject_placeholder",
"cohort": "15",
"campus": "서울 캠퍼스",
"authTime": 1781740800
}Backend response fields
ok?- 외부 앱 backend 처리 성공 여부
verified?- SSAFY 구성원 인증 완료 여부
sub?- client별 pairwise subject
cohort?- SSAFY 기수
campus?- SSAFY 캠퍼스
authTime?- JWT auth_time NumericDate seconds
보안 체크리스트
공식 지원 범위
protocol, hosted page, backend token exchange만 공식 지원
secret 위치
client_secret은 confidential client의 서버 환경변수에만 저장
state 검증
authorize 시작 시 만든 state와 callback state를 반드시 비교
PKCE 보관
code_verifier는 callback 완료 후 서버 교환까지만 짧게 보관
token 검증
verification_token은 서버에서 iss, aud, exp, sub, client_id, verified, auth_time, amr, acr를 검증
저장 최소화
앱 DB에는 verified, sub, cohort, campus, verifiedAt 정도만 저장
Mattermost id
ssafy.mattermost_id는 기존 MM 인증 계정 매핑 목적일 때만 요청하고 저장