적용관련 가이드

Passkey Server API

- 요청 메시지 정보

HTTP Method GET
HTTP URL https://svc.passkeys.kr/webauthn/api/mgmt/client/check-tx
HTTP Header Authorization 헤더에 Bearer 스키마에 JWT 토큰 설정
Accept application/json

- 요청 파라미터 명세

Parameter Type Required Description
serviceTxId string MUST 이용고객사 가입자의 callbackURL 파라미터로 전달된 결과를 조회하고자 하는 서비스 트랜잭션 식별자

- 요청 URL 예시

https://svc.passkeys.kr/webauthn/api/v1/clients/check-tx?serviceTxId=tx00001111aaaa

- 응답 파라미터 명세

응답은 json 형태로 전달 받으며 해당 파라미터에 대한 명세는 아래와 같다.

Parameter Type Required Description
code number MUST 서비스 트랜잭션 결과 요청에 대한 처리 결과 코드를 나타낸다. 값이 0이면 결과 요청 처리 성공을, 0이 아니면 이는 에러 코드를 나타낸다.
userHandle string OPTIONAL 서비스 트랜잭션을 요청한 이용고객사의 가입자 정보로 패스키에서 관리하는 식별 정보. code 값이 0인 경우 제공.
txType number OPTIONAL 이용고객사사 패스키 서버로 요청했던 서비스의 타입을 나타냄. - 2 : 패스키 서비스 등록 - 4 : 패스키 서비스 로그인(서명검증)
message string OPTIONAL code 값이 0이 아닌 경우 패스키 서버에서의 처리 실패 관련 메시지를 나타냄.

-응답 데이터 예시

{
"code" : 0,
"userHandle" : "user001",
"txType" : 2
}

{
"code":6002,
"message":"There is no client info.(serviceTxId : 25a0ab34e6c87dc606c3e977cd31e04ebd688ee85894dac7e0b4a470fb35793)"
}

메시지 인증을 위한 JWT 토큰 생성 절차 및 정보

패스키 서버는 이용고객사 서비스 서버로부터의 트랜잭션 결과 조회 요청에 대한 인증을 수행한다. 인증은 HTTP Header에 검증 가능한 토큰을 전달하여 패스키 서버가 검증함으로써 이루어진다. 토큰을 생성하기 위해서는 패스키 서비스 가입시 전달했던 최종 승인 메일에서 필요한 정보를 획득한다.
토큰은 JWT형태로 HTTP 메시지의 'Authorization' Header의 'Bearer' 스킴에 적용한다. JWT의 Header, Payload 구성은 아래와 같다.

HEADER

NAME VALUE DESC
alg HS256 JWT 알고리즘명(고정)
kid kid001 패스키 서비스 가입등록 후 부여 받은 key 식별자

PAYLOAD

CLAIM VALUE DESC
iss company 토큰 송신자로 가입시 등록한 Relying Party 이름
sub company 토큰 이름으로 서비스 가입시 등록한 Relying Party 이름
aud wv-passkey 패스키 서비스 이름(고정)
iat 1683713491 JWT 생성시간
exp 1683714694 JWT 만료시간(10분)

위 HEADER, PAYLOAD 정보를 바탕으로 패스키 서비스 가입등록 후 부여 받은 access_key 값을 이용하여 서명을 수행함으로써 JWT 값을 얻을 수 있다. 또한 위 요청은 반드시 패스키 서비스 가입등록시 설정했던 IP주소를 가진 서버에서 요청하여야 한다.

JWT 토큰값(예시)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJpc3MiOiJuZXZlcmRpZS5jbG91ZCIsInN1YiI6Im5ldmVyZGllLmNsb3VkIiwiYXVkIjoid3YtcGFzc2tleSIsImlhdCI6MTY4NTUxNDg5NiwiZXhwIjoxNjg1NTE1NDk2fQ.PFh__h0QSUwvxjr5T5JDuZaajl59SGBBZ8OZc4LLT_4

서비스 트랜잭션 결과 조회 요청 예제(JAVA)

이용고객사 서비스 서버(ex, 로그인 서버)가 구현해서 처리해야 하는 페이지로 이용고객사 가입자가 패스키 서버로부터 서비스 요청에 대한 응답으로 전달받아 호출하게 되는 callbackURL 페이지의 에제 코드이다.

<%@ page import="java.security.Key" %>
<%@ page import="io.jsonwebtoken.security.Keys" %>
<%@ page import="java.util.Date" %>
<%@ page import="io.jsonwebtoken.Jwts" %>
<%@ page import="io.jsonwebtoken.Header" %>
<%@ page import="io.jsonwebtoken.JwsHeader" %>
<%@ page import="io.jsonwebtoken.SignatureAlgorithm" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.net.HttpURLConnection" %>
<%@ page import="java.net.URL" %>
<%@ page import="com.fasterxml.jackson.databind.ObjectMapper" %>
<%@ page import="java.util.Map" %>
<%@ page import="javax.net.ssl.*" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="java.security.cert.CertificateException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


<%!
public static final String JWT_PAYLOAD_AUDIENCE_VALUE = "wv-passkey";
public static final String PASSKEY_SERVER_URL = "https://svc.passkeys.kr/webauthn/api/v1/clients/check-tx?serviceTxId=";
%>

<%
String serviceTxId = request.getParameter("serviceTxId"); //callbackURL로부터 파라미터 정보 추출
String accessKey = "12345678901234567890123456789011"; // 이용고객사 정보 등록에 대한 승인 응답 메일에서 획득(access_key)
String accessKeyId = "1"; // 이용고객사 정보 등록에 대한 승인 응답 메일에서 획득(access_key_id)
String issuer = "neverdie.cloud"; // 이용고객사 정보 등록에 대한 승인 응답 메일에서 획득(rp_name)
Key key = Keys.hmacShaKeyFor(accessKey.getBytes());
Date now = new Date();
Date expiredAt = new Date(now.getTime() + 10 * 60 * 1000);// 메시지 만료기간 10분 후
String jwt = Jwts.builder().setHeaderParam(JwsHeader.ALGORITHM, SignatureAlgorithm.HS256)
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setHeaderParam(JwsHeader.KEY_ID, accessKeyId)
.setIssuer(issuer)
.setSubject(issuer)
.setAudience(JWT_PAYLOAD_AUDIENCE_VALUE)
.setIssuedAt(now)
.setExpiration(expiredAt)
.signWith(key)
.compact();
// jwt = eyJhbGciOiJIUzI1NiIsInR.eymNsb3VkIiwiYXVkIjoid3YtcGFzc2tleSIsImlhdCI6MTY4E1NDk2fQ.PFh__h0QSUwvxjr5T5JDuZaaj
getTxResult(jwt, serviceTxId);
%>

<%!
public static void getTxResult(String jwt, String serviceTxId) {
try {
URL url = new URL(PASSKEY_SERVER_URL +serviceTxId);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();

conn.setRequestProperty("Authorization","Bearer" + jwt); // header의 Authoriztion 정보
conn.setRequestProperty("Accept","application/json"); //
conn.setRequestMethod("GET");
conn.setDoOutput(true);

// read data from server
BufferedReader br = newBufferedReader(newInputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;

while((line= br.readLine()) != null) {
sb.append(line);
}

String rspMsg = sb.toString();

// parse json reponse data
ObjectMapper om = new ObjectMapper();
Map< String, Object > map = om.readValue(rspMsg, Map.class);

int code = (Integer)map.get("code");
String userHandle;
int txType;
String errorMsg;

// proecessing based on result code
if( code != 0 ){
errorMsg = (String)map.get("message");
}else{
userHandle = (String)map.get("userHandle");
txType = (Integer)map.get("txType");
}
// userHandle은 패스키에서의 사용자를 식별하는 정보이며, 로그인 서버에서 실제 로그인을 수행한 이용고객사 가입자의 식별정보
// 는 별도 매핑하여 관리한다.
// txType은 패스키 사용을 위한 등록(2)과 로그인(4)을 구분해 주는 값이다. 등록시 이용고객사 가입자의 식별자와 userHandle을
// 매핑시키고, 로그인시 로그인 결과를 요청한 가입자가 응답으로 받은 userHandle에 매핑된 가입자인지 확인하는 용도로 사용될 수 있다.
} catch(Exception e) {
e.printStackTrace();
}
}
%>