The service implemented in the authorization-server subfolder together with the Nginx instance defined in the nginx
subfolder of this repository makes possible to integrate Energy Web DID
solution (https://energy-web-foundation.gitbook.io/energy-web/foundational-concepts/self-sovereign-identity#decentralized-identifiers-dids)
into any REST service that requires this kind of user authentication to be added without changing its source code.
sequenceDiagram
participant client
participant nginx
participant auth proxy
participant PDA as passport-did-auth
client->>nginx: /auth/login
nginx->>auth proxy: /auth/login
auth proxy ->> PDA: identity token
PDA-->>auth proxy: access token
auth proxy->>auth proxy: generate new tokens pair
auth proxy-->> nginx: tokens pair
nginx-->>client: tokens pair
client->>nginx: /any/backend/path
nginx->>auth proxy: /auth/token-introspection
auth proxy-->>nginx: OK
nginx->>backend: /any/backend/path
backend-->>nginx: response
nginx-->>client: response
client->>client: continue working with the backend API
client->>client: detect access token expired
client->>nginx: /auth/refresh-token
nginx->>auth proxy: /auth/refresh-token
auth proxy->>auth proxy: generate new tokens pair
auth proxy-->> nginx: tokens pair
nginx-->>client: tokens pair
client->>client: continue working with the backend API
- nodejs
- yarn
- docker
- docker compose
- jq (https://stedolan.github.io/jq/download/) (for development and exploring)
yarn installdocker-compose -f docker-compose.dev.yaml up --buildcd authorization-serverCopy .env.example file to .env file
Edit .env file and:
- set
JWT_SECRETto contain your secret phrase used to generate and validate tokens. - set
ACCEPTED_ROLESto contain roles that DIDs are required to be enrolled to
For the detailed env variables description check this document.
Execute yarn start:dev
If you need to test local version of passport-did-auth you can link it
cd authorization-server
yarn add passport-did-auth@link:../../passport-did-auth
And unlink it before commiting
Put a private key into the PRIVATE_KEY env variable:
export PRIVATE_KEY=<your private key here>Generate an identity token and store it in the IDENTITY_TOKEN env variable:
export IDENTITY_TOKEN=$(node generate-identity-cli/index.js -p $PRIVATE_KEY -b 999999999999)Now, you can request access and refresh tokens pair:
curl "http://localhost:8080/auth/login" \
-Ssf \
-X POST --header 'Content-Type: application/json' \
-d "{\"identityToken\": \"$IDENTITY_TOKEN\"}" \
| jqYou will see the following output:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUxZGI1Y2EyLTNlNTUtNDc5NC04N2U4LWNmMDM2YTNjYjBjMCIsImRpZCI6ImRpZDpldGhyOjB4ODJGY0IzMTM4NUVhQmUyNjFFNGU2MDAzYjlGMkNiMmFmMzRlMjY1NCIsInJvbGVzIjpbInJvbGUxLnJvbGVzLmFwcC10ZXN0Mi5hcHBzLmFydHVyLmlhbS5ld2MiXSwiaWF0IjoxNjQ0OTIzMjk5LCJleHAiOjE2NDQ5MjMzMDl9.XFR4V76W_6Ox8-ocVNDSGBNTLpdBNdo5kU1gvpnovOs",
"type": "Bearer",
"expires_in": 9,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjFkMTQ0M2JkLWFkOTktNGZhZC04ZTYyLTVmOGVlMzI2MWQ5YiIsImRpZCI6ImRpZDpldGhyOjB4ODJGY0IzMTM4NUVhQmUyNjFFNGU2MDAzYjlGMkNiMmFmMzRlMjY1NCIsInJvbGVzIjpbInJvbGUxLnJvbGVzLmFwcC10ZXN0Mi5hcHBzLmFydHVyLmlhbS5ld2MiXSwiaWF0IjoxNjQ0OTIzMjk5LCJleHAiOjE2NDQ5MjM4OTl9.1n8TiG1cPSZEfJdj209TQylWqKyU2BDXHUX4loGyggU"
}You can store generated access_token in the ACCESS_TOKEN env variable to be used in requests to the actual REST API:
export ACCESS_TOKEN=$(curl "http://localhost:8080/auth/login" \
-Ssf \
-X POST --header 'Content-Type: application/json' \
-d "{\"identityToken\": \"$IDENTITY_TOKEN\"}" \
| jq -r .access_token)Request an endpoint with the valid access token:
curl http://127.0.0.1:8080/any-path -H "Authorization: Bearer $ACCESS_TOKEN"You will see the following response created by the backend service:
{ "message": "backend response", "timestamp": "2022-02-15T11:15:24.904Z" }Request an endpoint with invalid token:
curl -v http://127.0.0.1:8080/ -H "Authorization: Bearer invalid-token"You will see the following response generated by the NginX and 401 http response status code:
<html>
<head>
<title>401 Authorization Required</title>
</head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr />
<center>nginx/1.21.6</center>
</body>
</html>After your access token expires, you need to regenerate it by providing your refresh token in the following request:
curl -X POST 'http://localhost:8080/auth/refresh-token' \
-H 'Content-Type: application/json' \
--data-raw '{
"refreshToken": "{{your refresh token here}}"
}'You will get the same response as in case of /auth/login endpoint if your refresh token has not expired yet.
To run this solution on production, you need to build docker images:
build:dockerAdjust docker-compose.yaml or create any other orchestrator configuration you use to:
- replace
backendservice with your REST API service. Probably, you will need to also adjustnginx/nginx.conffile before building the images to contain hostname of your service if it is not defined by your orchestration solution - contain correct JWT_SECRET value
- contain correct ACCEPTED_ROLES value
- finetune any other env variables for
auth-serverservice to meet your needs