Authentication
10 min
primevault authenticates every external api request with an api user key and a short lived es256 bearer token authentication is organization scoped credentials may access only the organization and permissions assigned to that api user base url https //api primevault com all endpoint paths are relative to this url recommended use the javascript sdk the sdk signs the exact request url and json body, adds the required headers, and maps http failures to typed errors import { apiclient } from "@primevault/js api sdk"; const apiclient = new apiclient( process env primevault api key!, "https //api primevault com", process env primevault access private key!, ); create the client only in a trusted server environment never embed the api key or access private key in browser, mobile, or other distributed client code generate a bearer token manually use manual generation only when the official sdk cannot own the http request the token is request bound generate it from the relative url path and the same body object that will be sent, then create a new token for the next request the examples below accept either a pem private key or a hex encoded pkcs#8 der private key they intentionally use the der encoded ecdsa signature produced by the primevault sdks do not replace the signing step with a generic jwt helper unless it can emit that signature format javascript (node js) import { createhash, createprivatekey, createsign, randomuuid, } from "node\ crypto"; const sortkeys = (value) => { if (array isarray(value)) return value map(sortkeys); if (value && typeof value === "object") { return object fromentries( object keys(value) sort() map((key) => \[key, sortkeys(value\[key])]), ); } return value; }; const base64url = (value) => buffer from(value) tostring("base64url"); function generateprimevaulttoken(apikey, privatekey, urlpath, body = {}) { const now = math floor(date now() / 1000); const bodyhash = createhash("sha256") update(json stringify(sortkeys(body))) digest("hex"); const header = { alg "es256", typ "jwt" }; const payload = { iat now, exp now + 120, urlpath, userid apikey, body bodyhash, jti randomuuid(), }; const signinginput = \[header, payload] map((part) => base64url(json stringify(sortkeys(part)))) join(" "); const trimmedkey = privatekey trim(); const key = trimmedkey startswith(" begin") ? createprivatekey(trimmedkey) createprivatekey({ key buffer from(trimmedkey, "hex"), format "der", type "pkcs8", }); const signer = createsign("sha256"); signer update(signinginput); signer end(); return `${signinginput} ${base64url(signer sign(key))}`; } const apikey = process env primevault api key; const privatekey = process env primevault access private key; const urlpath = "/api/external/vaults/"; const token = generateprimevaulttoken(apikey, privatekey, urlpath); const headers = { authorization `bearer ${token}`, "api key" apikey, accept "application/json", }; python import base64 import hashlib import json import os import time import uuid from typing import optional from cryptography hazmat primitives import hashes, serialization from cryptography hazmat primitives asymmetric import ec def base64url(value bytes) > str return base64 urlsafe b64encode(value) decode("ascii") def generate primevault token( api key str, private key str, url path str, body optional\[dict] = none, ) > str now = int(time time()) canonical body = json dumps( body or {}, sort keys=true, separators=(",", " "), ensure ascii=false, ) header = {"alg" "es256", "typ" "jwt"} payload = { "iat" now, "exp" now + 120, "urlpath" url path, "userid" api key, "body" hashlib sha256(canonical body encode()) hexdigest(), "jti" str(uuid uuid4()), } signing input = " " join( base64url( json dumps( part, sort keys=true, separators=(",", " "), ensure ascii=false, ) encode() ) for part in (header, payload) ) trimmed key = private key strip() if trimmed key startswith(" begin") key = serialization load pem private key( trimmed key encode(), password=none, ) else key = serialization load der private key( bytes fromhex(trimmed key), password=none, ) signature = key sign( signing input encode(), ec ecdsa(hashes sha256()), ) return f"{signing input} {base64url(signature)}" api key = os environ\["primevault api key"] private key = os environ\["primevault access private key"] url path = "/api/external/vaults/" token = generate primevault token(api key, private key, url path) headers = { "authorization" f"bearer {token}", "api key" api key, "accept" "application/json", } for post and put requests, construct the body once, pass that object to the token helper, and send the same object as json without mutating its keys or values between signing and transmission required headers header value purpose authorization bearer \<signed jwt> short lived es256 token bound to the exact request path and, for writes, the exact json body api key api user key identifies the api user and organization version installed sdk version identifies the client contract added by the sdk content type application/json required when a json request body is sent accept application/json requests a json response added by the sdk header names are case insensitive at the http layer, but use the spelling above in examples and diagnostics how request signing works build the relative request url, excluding the scheme and host the query string may be included; the verifier compares the path component canonically serialize the request body with recursively sorted keys and compact json separators use an empty object for a request without a body hash that canonical body with sha 256 and create a short lived payload containing iat , exp , urlpath , userid , body , and a unique jti base64url encode the header and payload, then sign header payload with p 256 and sha 256 send the resulting bearer token with the same api user key in the api key header generate a new token for every request changing the path, body keys, or body values after token generation invalidates the request binding credential and key safety store credentials in a secret manager or protected server environment variables do not log bearer tokens, api keys, private keys, or complete bank account details rotate credentials immediately if they may have been exposed keep server time synchronized so short lived token timestamps remain valid restrict source ips where appropriate; see api user ip whitelisting use the least privileged api user required by the integration authentication failures http status meaning what to check 401 authentication failed missing or expired token, wrong api key, mismatched path/body signature, or clock skew 403 authenticated but not permitted api user role, policy, vault permission, organization scope, sub organization scope, or ip allowlist do not treat a 403 as a token refresh problem verify authorization and resource scope before retrying setup references use setting up api user to provision credentials and api user ip whitelisting to restrict allowed source addresses