All requests from BRIJ to your endpoints (API calls and webhooks) include a cryptographic signature in the
X-BRIJ-Signature HTTP header. This signature is a JSON Web Token (JWT) signed with BRIJ's private key using the RS256
algorithm.
By verifying this signature, you can ensure that:
The request originated from BRIJ
The request payload has not been modified in transit
importjwtfrom'jsonwebtoken';importcryptofrom'crypto';// BRIJ public key (use appropriate key for your environment)constBRIJ_PUBLIC_KEY=`-----BEGIN PUBLIC KEY-----[YOUR_ENVIRONMENT_PUBLIC_KEY]-----END PUBLIC KEY-----`;constYOUR_PARTNER_ID='your-partner-id';interfaceBrijJwtPayload{iss:string;aud:string;iat:number;exp:number;jti:string;payload_hash:string;}functionverifyBrijRequest(signatureHeader:string,requestBody:Buffer|string):{valid:boolean;jti?:string;error?:string}{try{// 1. Verify JWT signature and decodeconstdecoded=jwt.verify(signatureHeader,BRIJ_PUBLIC_KEY,{algorithms:['RS256'],issuer:'brij.fi',audience:YOUR_PARTNER_ID,})asBrijJwtPayload;// 2. Verify payload hashconstbodyBuffer=typeofrequestBody==='string'?Buffer.from(requestBody):requestBody;constcalculatedHash=crypto.createHash('sha256').update(bodyBuffer).digest('hex');if(calculatedHash!==decoded.payload_hash){return{valid:false,error:'Payload hash mismatch'};}// 3. Return success with jti for idempotency trackingreturn{valid:true,jti:decoded.jti};}catch(error){return{valid:false,error:errorinstanceofError?error.message:'Unknown error'};}}// Express.js middleware exampleimport{Request,Response,NextFunction}from'express';functionbrijSignatureMiddleware(req:Request,res:Response,next:NextFunction){constsignature=req.headers['x-brij-signature']asstring;if(!signature){returnres.status(401).json({error:'Missing X-BRIJ-Signature header'});}// Note: You need raw body access - configure express accordinglyconstresult=verifyBrijRequest(signature,req.body);if(!result.valid){returnres.status(401).json({error:result.error});}// Optionally check jti for replay protection// if (seenJtis.has(result.jti)) {// return res.status(409).json({ error: 'Duplicate request' });// }// seenJtis.add(result.jti);next();}
importhashlibimportjwtfromtypingimportTuple,Optional# BRIJ public key (use appropriate key for your environment)BRIJ_PUBLIC_KEY="""-----BEGIN PUBLIC KEY-----[YOUR_ENVIRONMENT_PUBLIC_KEY]-----END PUBLIC KEY-----"""YOUR_PARTNER_ID="your-partner-id"defverify_brij_request(signature_header:str,request_body:bytes)->Tuple[bool,Optional[str],Optional[str]]:""" Verify a BRIJ request signature. Returns: Tuple of (is_valid, jti, error_message) """try:# 1. Verify JWT signature and decodedecoded=jwt.decode(signature_header,BRIJ_PUBLIC_KEY,algorithms=["RS256"],issuer="brij.fi",audience=YOUR_PARTNER_ID,)# 2. Verify payload hashcalculated_hash=hashlib.sha256(request_body).hexdigest()ifcalculated_hash!=decoded["payload_hash"]:returnFalse,None,"Payload hash mismatch"# 3. Return success with jti for idempotency trackingreturnTrue,decoded["jti"],Noneexceptjwt.ExpiredSignatureError:returnFalse,None,"Token expired"exceptjwt.InvalidAudienceError:returnFalse,None,"Invalid audience"exceptjwt.InvalidIssuerError:returnFalse,None,"Invalid issuer"exceptjwt.InvalidSignatureError:returnFalse,None,"Invalid signature"exceptExceptionase:returnFalse,None,str(e)# ------------------------------# Flask example# ------------------------------fromflaskimportFlask,request,jsonifyflask_app=Flask(__name__)@flask_app.before_requestdefverify_brij_signature():# Skip verification for non-BRIJ endpointsifnotrequest.path.startswith("/webhook"):returnsignature=request.headers.get("X-BRIJ-Signature")ifnotsignature:returnjsonify({"error":"Missing X-BRIJ-Signature header"}),401is_valid,jti,error=verify_brij_request(signature,request.get_data())ifnotis_valid:returnjsonify({"error":error}),401# Optionally store jti in request context for replay protectionrequest.brij_jti=jti# ------------------------------# FastAPI example (alternative)# ------------------------------fromfastapiimportFastAPI,Request,HTTPException,Dependsfastapi_app=FastAPI()asyncdefverify_brij_signature(request:Request)->str:signature=request.headers.get("X-BRIJ-Signature")ifnotsignature:raiseHTTPException(status_code=401,detail="Missing X-BRIJ-Signature header")body=awaitrequest.body()is_valid,jti,error=verify_brij_request(signature,body)ifnotis_valid:raiseHTTPException(status_code=401,detail=error)returnjti@fastapi_app.post("/webhook")asyncdefwebhook(request:Request,jti:str=Depends(verify_brij_signature)):# Process webhook...return{"status":"ok","request_id":jti}
packagemainimport("bytes""context""crypto/sha256""encoding/hex""fmt""io""net/http""github.com/golang-jwt/jwt/v5")// BRIJ public key (use appropriate key for your environment)varbrijPublicKeyPEM=`-----BEGIN PUBLIC KEY-----[YOUR_ENVIRONMENT_PUBLIC_KEY]-----END PUBLIC KEY-----`constyourPartnerID="your-partner-id"typeBrijClaimsstruct{PayloadHashstring`json:"payload_hash"`jwt.RegisteredClaims}funcVerifyBrijRequest(signatureHeaderstring,requestBody[]byte)(string,error){// Parse the public keypublicKey,err:=jwt.ParseRSAPublicKeyFromPEM([]byte(brijPublicKeyPEM))iferr!=nil{return"",fmt.Errorf("failed to parse public key: %w",err)}// Parse and verify the JWTtoken,err:=jwt.ParseWithClaims(signatureHeader,&BrijClaims{},func(token*jwt.Token)(interface{},error){// Verify the signing methodif_,ok:=token.Method.(*jwt.SigningMethodRSA);!ok{returnnil,fmt.Errorf("unexpected signing method: %v",token.Header["alg"])}returnpublicKey,nil},)iferr!=nil{return"",fmt.Errorf("failed to parse token: %w",err)}claims,ok:=token.Claims.(*BrijClaims)if!ok||!token.Valid{return"",fmt.Errorf("invalid token")}// Verify issuerifclaims.Issuer!="brij.fi"{return"",fmt.Errorf("invalid issuer: %s",claims.Issuer)}// Verify audienceif!claims.VerifyAudience(yourPartnerID,true){return"",fmt.Errorf("invalid audience")}// Verify payload hashhash:=sha256.Sum256(requestBody)calculatedHash:=hex.EncodeToString(hash[:])ifcalculatedHash!=claims.PayloadHash{return"",fmt.Errorf("payload hash mismatch")}returnclaims.ID,nil// Return jti for idempotency tracking}// HTTP middleware examplefuncBrijSignatureMiddleware(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){signature:=r.Header.Get("X-BRIJ-Signature")ifsignature==""{http.Error(w,"Missing X-BRIJ-Signature header",http.StatusUnauthorized)return}// Read the bodybody,err:=io.ReadAll(r.Body)iferr!=nil{http.Error(w,"Failed to read body",http.StatusBadRequest)return}// Verify the signaturejti,err:=VerifyBrijRequest(signature,body)iferr!=nil{http.Error(w,err.Error(),http.StatusUnauthorized)return}// Optionally check jti for replay protection// if seenJtis[jti] {// http.Error(w, "Duplicate request", http.StatusConflict)// return// }// seenJtis[jti] = true// Store body back for handler to readr.Body=io.NopCloser(bytes.NewBuffer(body))// Add jti to context if neededctx:=context.WithValue(r.Context(),"brij_jti",jti)next.ServeHTTP(w,r.WithContext(ctx))},)}