...
...
...
...
| Initial private (JWT) key registration processThe private_key_jwt authentication is based on asymmetric key, the private part is generated and only known by the client whereas the public part is communicated and registered in the authorization server for the specific client. draw.io Diagram |
---|
border | true |
---|
| |
---|
diagramName | private_key_jwt registration |
---|
simpleViewer | false |
---|
|
|
|
...
links | auto |
---|
tbstyle | top |
---|
lbox | true |
---|
diagramWidth | 1081 |
---|
revision | 1 |
---|
|
|
|
Generation of ES256 asymmetric keys
openssl command can be used to generate ES256 asymmetric keys as required by the FAPI part 2 specification. Office will keep the private key and share the public key for registration with WIPO. Below is an example of script to generate ES256 asymmetric keys.
Code Block |
---|
language | bash |
---|
theme | RDark |
---|
title | private_key_jwt generation script |
---|
collapse | true |
---|
|
#!/bin/bash
# Set the environment
PRIVATE_KEY_ES256=es256_private.pem
PUBLIC_KEY_ES256=es256_public.pem
CLIENT_NAME=DAS
# Generates the ES256 keys
openssl ecparam -genkey -name prime256v1 -noout -out "${PRIVATE_KEY_ES256}"
# Extracts the public key
openssl ec -in "${PRIVATE_KEY_ES256}" -pubout -out "${PUBLIC_KEY_ES256}"
# Generates an x509 certificate
CERT_KEY_ES256=es256_cert.pem
OPENSSL_CONF=./openssl.cnf
CERT_CN="${CLIENT_NAME} private_key_jwt authentication"
# Build the certificate config file
printf '[ req ]\n' > "${OPENSSL_CONF}"
printf 'prompt = no\n' >> "${OPENSSL_CONF}"
printf 'distinguished_name = req_distinguished_name\n' >> "${OPENSSL_CONF}"
printf '[ req_distinguished_name ]\n' >> "${OPENSSL_CONF}"
printf 'CN = %s\n' "${CERT_CN}" >> "${OPENSSL_CONF}"
# Creates the x509 certificate
openssl req -x509 -new -config "${OPENSSL_CONF}" -key "${PRIVATE_KEY_ES256}" -out "${CERT_KEY_ES256}" |
...
Files
...
Description
...
Private (JWT) key authentication |
|
...
processThe authentication flow of private_key_jwt is depicted in the diagram below draw.io Diagram |
---|
border | true |
---|
| |
---|
diagramName | private_key_jwt authentication |
---|
simpleViewer | false |
---|
width | 600 |
---|
links | auto |
---|
tbstyle | top |
---|
lbox | true |
---|
diagramWidth | 1121 |
---|
revision | 1 |
---|
|
The private_key_jwt authentication consists of creating a JSON structure containing the following login attributes: Attribute | Example | Description |
---|
iss | das-api-auth | Issuer: Client id | sub | das-api-auth | Subject: Client id | aud | https://logindev.wipo.int:443/am/oauth2/access_token | Audience: Token endpoint of the authorization server | exp | 1622450728 | Expiration time: The expiration time of the data, current time + small amount of seconds (current epoch + 10s is ok) |
ES256 signing algorithm + above attributes + signature of them must be served in JWT format (rfc7519), see below: JWT client assertion header Code Block |
---|
language | yml |
---|
linenumbers | true |
---|
| { |
|
|
...
...
...
JWT client assertion payload |
|
...
...
...
"aud": "https://logindev.wipo.int:443/am/oauth2/access_token", |
|
|
...
...
JWT client assertion signature Code Block |
---|
| # Signature of the header and payload sections.
# it is an array of bytes encoded in base 64 |
All parts are encoded and separated by '.' to make up the JWT as follows private_key_jwt assertion Code Block |
---|
| eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9. |
|
|
...
eyJpc3MiOiJkYXMtYXBpLWF1dGgiLCJz
dWIiOiJkYXMtYXBpLWF1dGgiLCJhdWQiOiJodHRwczovL2xvZ2luZGV2LndpcG8uaW50O
jQ0My9hbS9vYXV0aDIvYWNjZXNzX3Rva2VuIiwiZXhwIjoxNjIyNDUwNzI4fQ.BLA6k2
kKKFVm6AG-DPDpRU_5JDFGRF1dHjKul7saWCv5OxXGg4EY- |
|
|
...
J9e1p8Dg0ngD2dZ2grkJ2
su7jaHy67YEw |
The JWT client assertion can now be submitted to authorization server for authentication with the token endpoint (i.e. POST https://logindev.wipo.int:443/am/oauth2/access_token in the attached specification) including the following parameters for client_credentials: POST parameter | example | Description |
---|
grant_type | client_credentials | OAuth2 client_credentials authentication flow is used for machine to machine communication | scope | das-api/das-access | Scopes (=roles), if any, separated by spaces which are required to use the DAS API | client_assertion_type | urn:ietf:params:oauth:client-assertion-type:jwt-bearer | The client_assertion_type indicates to the authorization server the method used to authenticate, private_key_jwt requires jwt-bearer | client_assertion | eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkYXMtYXBpLWF1dG giLCJzdWIiOiJkYXMtYXBpLWF1dGgiLCJhdWQiOiJodHRwczovL2xvZ2luZG V2LndpcG8uaW50OjQ0My9hbS9vYXV0aDIvYWNjZXNzX3Rva2VuIiwiZXh wIjoxNjIyNDUwNzI4fQ.BLA6k2kKKFVm6AG-DPDpRU_5JDFGRF1dHjKul7saWCv5OxXGg4EY-J9e1p8Dg0ngD2dZ2grkJ2su7jaHy67YEw | The JWT generated in the paragraph above |
Note |
---|
The above token endpoint is part of third party product that supports OpenID Connect (OIDC) authentication protocol based on the OAuth 2.0 family of specifications |
|
|
Below is an example of authentication script.
Code Block |
---|
theme | RDark |
---|
title | private_key_jwt authentication script |
---|
collapse | true |
---|
|
#!/bin/bash
PRIVATE_KEY_ES256=es256_private.pem
CLIENT_ID=das-api-auth
SCOPE="das-api/das-access"
ISSUER="https://logindev.wipo.int/am/oauth2"
# https://logindev.wipo.int/am/oauth2/.well-known/openid-configuration
OIDC_CONFIG_JSON=$(curl -k "${ISSUER}/.well-known/openid-configuration")
# Generic way to obtain the token endpoint
TOKEN_ENDPOINT=$(printf '%s' ${OIDC_CONFIG_JSON} | jq -r ".token_endpoint")
UTC_TIME=$(date -u +%s)
EXP_TIME=$(expr "$UTC_TIME" + 10)
JSON='{'
JSON=${JSON}$(printf '"iss":"%s"' ${CLIENT_ID})
JSON=${JSON}$(printf ',"sub":"%s"' ${CLIENT_ID})
JSON=${JSON}$(printf ',"aud":"%s"' ${TOKEN_ENDPOINT})
JSON=${JSON}$(printf ',"exp":%s' ${EXP_TIME})
JSON=${JSON}'}'
JSON_HEADER_B64=$(printf '{"alg":"ES256","typ":"JWT"}' | jq -cj | base64 -w0 | tr -d '\n=' | tr '+/' '-_')
JSON_PAYLOAD_B64=$(printf $JSON | jq -cj | base64 -w0 | tr -d '\n=' | tr '+/' '-_')
JSON_SIGNATURE_ASN1_B64=$(printf '%s.%s' $JSON_HEADER_B64 $JSON_PAYLOAD_B64 | openssl dgst -sha256 -sign "${PRIVATE_KEY_ES256}" | openssl asn1parse -inform DER | base64 -w0)
JSON_SIGNATURE_HEX=$(printf $JSON_SIGNATURE_ASN1_B64 | base64 -d | sed -n '/INTEGER/p' | sed 's/.*INTEGER\s*://g' | sed -z 's/[^0-9A-F]//g')
JSON_SIGNATURE_B64=$(printf $JSON_SIGNATURE_HEX | xxd -p -r | base64 -w0 | tr -d '\n=' | tr '+/' '-_')
JWT_ASSERTION=$(printf '%s.%s.%s' $JSON_HEADER_B64 $JSON_PAYLOAD_B64 $JSON_SIGNATURE_B64)
# echo $JWT_ASSERTION
# Access token private_key_jwt
curl --insecure \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode scope="${SCOPE}" \
--data-urlencode "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
--data-urlencode "client_assertion=${JWT_ASSERTION}" \
--url "${TOKEN_ENDPOINT}" |
The output of the script is as follows:
private_key_jwt authentication output
...
{
"access_token"
:
"eyJ0eXAiOiJKV1QiLCJraWQiOiJmVWRmbEJSa3c5bm1tejcrL3BmMWM5d2RYdXc9IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkYXMtYXBpLWF1dGgiLCJjdHMiOiJPQVVUSDJfU1RBVEVMRVNTX0dSQU5UIiwiYXVkaXRUcmFja2luZ0lkIjoiMTQyYjMwODEtZDNjNy00MjJjLWI4ZDQtNjU4NjkwNjVmMzQ4LTU0OTkxIiwiaXNzIjoiaHR0cHM6Ly9sb2dpbmRldi53aXBvLmludDo0NDMvYW0vb2F1dGgyIiwidG9rZW5OYW1lIjoiYWNjZXNzX3Rva2VuIiwidG9rZW5fdHlwZSI6IkJlYXJlciIsImF1dGhHcmFudElkIjoibko4bmh5bEM4S3g5RFk4bDJTSGxvcHdDZmJnIiwiYXVkIjoiZGFzLWFwaS1hdXRoIiwibmJmIjoxNjIyNDU0OTUzLCJncmFudF90eXBlIjoiY2xpZW50X2NyZWRlbnRpYWxzIiwic2NvcGUiOlsiZGFzLWFwaS9kYXMtYWNjZXNzIl0sImF1dGhfdGltZSI6MTYyMjQ1NDk1MywicmVhbG0iOiIvIiwiZXhwIjoxNjIyNDU4NTUzLCJpYXQiOjE2MjI0NTQ5NTMsImV4cGlyZXNfaW4iOjM2MDAsImp0aSI6InJvRzhtcWE4WjFaM0YwME1kMjB2VW95aEEwSSJ9.d1EEdioprD2AxQxQcVj0zlN8hvSaIdtub0Lk887m52qEKFt9YiW3uGhpw8bMnhwsUyBbbdFq1flA3pVdKYAdNhQ2dRBIemTH8_NjA4l4giGpLeKJ7WRQA-ldsWrrLkLkVu7gbx7TmMLrTkXgL17kiLdPQ44S1O6LKX52v3KkT0XYEyMYIuzYlnMBs1GQWkoJEALZVIH3TtaAG22o4dxlCcMVxUCo-SyOctjRkfmLvuKEXpDvAG2F93o61Mz1sOtSC2m6nBQA9zd3MxtNd5vd0791QH16Of53IozPj7jRXblYCYq9SJyXzdHN7IEJWrT7C1vvwFVnq8c8QArKsMmgBw"
,
"scope"
:
"das-api/das-access"
,
"token_type"
:
"Bearer"
,
"expires_in"
: 3599
}
Access_token attributes like signature, validity, audience and scopes must be verified by the client, similarly DAS API must also verify the access_token and must additionally check if the client id (=sub claim) is authorized. DAS API must maintain the whitelisted clients
...
{
"sub"
:
"das-api-auth"
,
"cts"
:
"OAUTH2_STATELESS_GRANT"
,
"auditTrackingId"
:
"142b3081-d3c7-422c-b8d4-65869065f348-54991"
,
"iss"
:
"https://logindev.wipo.int:443/am/oauth2"
,
"tokenName"
:
"access_token"
,
"token_type"
:
"Bearer"
,
"authGrantId"
:
"nJ8nhylC8Kx9DY8l2SHlopwCfbg"
,
"aud"
:
"das-api-auth"
,
"nbf"
: 1622454953,
"grant_type"
:
"client_credentials"
,
"scope"
: [
"das-api/das-access"
],
"auth_time"
: 1622454953,
"realm"
:
"/"
,
"exp"
: 1622458553,
"iat"
: 1622454953,
"expires_in"
: 3600,
"jti"
:
"roG8mqa8Z1Z3F00Md20vUoyhA0I"
}
Warning |
---|
title | Certificate to communicate to WIPO |
---|
| After the generation of the es256 key, you will obtain the following files (or similar): es256_cert.pem | Certificate file that will be communicated to WIPO for the configuration of the private_key_jwt client authentication | es256_private.pem | Must never be communicated and kept secret, used by the client to sign the private_key_jwt authentication request | es256_public.pem | Used only at the generation step, kept for record | openssl.cnf | Used only at the generation step, kept for record |
As per the process, in order to register the Office key on WIPO systems, be aware that the file that has to be sent to WIPO is the one for the certificate (not the public key file) |
|
Column |
---|
| a.- New DAS exchange API Page Tree |
---|
root | a.- New DAS exchange API |
---|
startDepth | 0 |
---|
page | a.- Initial private_key_jwt registration process |
---|
|
|
|
...