Pomerium JavaScript SDK
Learn how to use Pomerium's JavaScript SDK to verify and parse JWTs issued by the authorization service.
Background
Pomerium is designed to sit in front of internal backend applications and services so that it can provide context-aware access management in a uniform way.
Although you can configure Pomerium to secure upstream applications with assertion headers (X-Pomerium-JWT-Assertion
) and Pomerium’s well-known JWKS endpoint (/.well-known/pomerium/jwks.json
), it’s not immediately obvious or easy for application developers to implement.
Pomerium’s JavaScript SDK offers both client- and server-side solutions that make it easier for developers to verify JWT assertion headers in upstream applications.
How to use the JavaScript SDK
The JavaScript SDK is available as an NPM package.
To use the JavaScript SDK, you need:
- React app
- Express server
Secure a React app with Pomerium
- Bootstrap a React application using Create React App
npx create-react-app react-app
cd react-app
- Add dependencies
yarn add
- Install the JavaScript SDK
yarn add @pomerium/js-sdk
- Go to
App.js
and add the following code
import { useEffect, useState } from 'react';
import { PomeriumVerifier, signOut } from '@pomerium/js-sdk';
function App() {
const [jwt, setJwt ] = useState('');
useEffect(() => {
const jwtVerifier = new PomeriumVerifier({
issuer: 'react.localhost.pomerium.io',
audience: 'react.localhost.pomerium.io',
expirationBuffer: 1000
});
jwtVerifier.verifyBrowserUser()
.then(r => setJwt(r))
.catch(e => console.log(e));
}, [])
return (
<div style={{margin: '20px'}}>
<pre>{JSON.stringify(jwt, null, 2)}</pre>
<div style={{marginTop: '20px'}}>
<button onClick={() => signOut('https://www.pomerium.io')} type="button">Sign Out Test</button>
</div>
</div>
);
}
export default App;
Trust on first use (TOFU)
The issuer
and audience
parameters are optional. If you don’t define them, PomeriumVerifier
applies firstUse
by default to the JWT provided by the identity provider. PomeriumVerifier
verifies subsequent requests with these claims.
If you define the issuer
and audience
parameters, PomeriumVerifier
verifies their values against the claims provided by the identity provider.
The issuer
and audience
will both be set to the URL of the upstream application without the prefixed protocol (for example, httpbin.corp.example.com
).
- Run
yarn start
You should see your React app in the browser.
Secure an Express server with Pomerium
- Create a directory for your Express server
mkdir express-server
cd express-server
- Initialize Node.js
npm init
- Add Express and the JavaScript SDK
yarn add express @pomerium/js-sdk
- Create an index.js file
touch index.js
- In
index.js
, add the following code
const express = require("express");
const { PomeriumVerifier } = require('@pomerium/js-sdk');
const app = express();
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; //just for dev
app.get("/tofu", (request, response) => {
const jwtVerifier = new PomeriumVerifier({});
jwtVerifier.verifyJwt(request.get('X-Pomerium-Jwt-Assertion')).then(r => response.send(r))
});
app.get("/wrong-audience", (request, response) => {
const jwtVerifier = new PomeriumVerifier({
audience: [
'correct-audience.com'
]
});
jwtVerifier.verifyJwt(request.get('X-Pomerium-Jwt-Assertion'))
.then(r => response.send(r))
.catch(e => response.send(e.message));
});
app.get("/wrong-issuer", (request, response) => {
const jwtVerifier = new PomeriumVerifier({
issuer: 'correct-issuer.com'
});
jwtVerifier.verifyJwt(request.get('X-Pomerium-Jwt-Assertion'))
.then(r => response.send(r))
.catch(e => response.send(e.message));
});
app.get("/expired", (request, response) => {
const jwtVerifier = new PomeriumVerifier({
expirationBuffer: -10000
});
jwtVerifier.verifyJwt(request.get('X-Pomerium-Jwt-Assertion'))
.then(r => response.send(r))
.catch(e => response.send(e.message));
});
app.listen(3010, () => {
console.log("Listen on the port 3010...");
});
Trust on first use (TOFU)
The issuer
and audience
parameters are optional. If you don’t define them, PomeriumVerifier
applies firstUse
by default to the JWT provided by the identity provider. PomeriumVerifier
verifies subsequent requests with these claims.
If you define the issuer
and audience
parameters, PomeriumVerifier
verifies their values against the claims provided by the identity provider.
The issuer
and audience
will both be set to the URL of the upstream application without the prefixed protocol (for example, httpbin.corp.example.com
).
- Run your server
node index.js
Configure a route in Pomerium
Add a route in Pomerium.
- Core
- Enterprise
routes:
- from: https://react.localhost.pomerium.io:4443
to: http://localhost:3000
pass_identity_headers: true
policy:
- allow:
or:
- email:
is: user@example.com
Create a policy:
- Go to Policies and enter a Name
- In the Create Policy sidebar, select Builder
- Select ADD ALLOW BLOCK and add an OR operator
- In the Criteria field, select Email
- In the Value field, enter the email associated with your identity provider
- Save the policy
Build a route:
- Go to Routes and select NEW ROUTE
- Enter a Name
- In the From field, enter the relevant URL:
https://react.localhost.pomerium.io:4443
https://express.localhost.pomerium.io/4443/tofu
- In the To field, enter
http://localhost:3000
- Select Pass Identity Headers
- Select Policies
- Select the policy you want to use with your route
- Save the route
If you're using more than one machine, deploy your React app and use the associated IP address instead of localhost.
Verify the JWT
In your browser, navigate to the relevant URL:
https://react.localhost.pomerium.io:4443
https://express.localhost.pomerium.io:443/tofu
If your JWT is valid, you should see a JSON payload with the following claims:
{
"payload": {
"aud": "react.localhost.pomerium.io",
"email": /* redacted */
"exp": 1685477549,
"groups": [],
"iat": 1685477249,
"iss": "react.localhost.pomerium.io",
"jti": "d6e514e9-f8a6-49bb-a5c4-258391d19e4d",
"name": /* redacted */
"sid": "d6e514e9-f8a6-49bb-a5c4-258391d19e4d",
"sub": "00u1gkwtecq0Mj1u04x7",
"user": "00u1gkwtecq0Mj1u04x7"
},
"protectedHeader": {
"alg": "ES256",
"kid": "997cf46e7e54de7e077419a5bf467dc1f711a96b2bf71d6de2ba35d07f29c3a2",
"typ": "JWT"
},
"key": {}
}
If the JWT is not valid or something wasn't implemented correctly, you should receive an error:
Resources: