Skip to content

Signed Results ‚Äč

When using Pass Requests for load-bearing identity and authentication, it's critical to verify the user is who they say they are.

We can do this by using a SignedRequestTopic to wrap our example topic from Recognizing Users, which will sign the result for verification in the requesting app.

typescript
import { RequestTopic, SignedRequestTopic } from '@passes/reqs';
import type { SignedBodyWrapper, SignedBodyWrapperHeader } from '@passes/reqs';

// The profile info fields that can be requested
type ProfileInfoType = 'email' | 'profile.name' | 'profile.picture';

// Array of requested profile info fields
type UserProfileRequest = ProfileInfoType[];
// Record from permission type to value
type UserProfileResult = Record<ProfileInfoType, string>;

// For this demo implementation, we'll use the SubtleCrypto API to sign and verify the result
const keypair = await crypto.subtle.generateKey(keyParams, true, ['sign', 'verify']);
const keyFormat = 'jwk';
const keyParams = { name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-384' };

// Wrap the RequestTopic from the previous example in a SignedRequestTopic
const requestUserProfile = new SignedRequestTopic<UserProfileRequest, UserProfileResult>({
  requestTopic: new RequestTopic({
    id: 'org.passes.example.signed-request-user-profile',
    requestBodyCodec: Codecs.Json,
    resultBodyCodec: Codecs.Json,
  }),
  signResult: async (body: boolean): Promise<SignedBodyWrapperHeader> => ({
    publicKey: await crypto.subtle.exportKey(
      keyFormat,
      keyPair.value.publicKey
    ),
    signature: new Uint8Array(await crypto.subtle.sign(
      keyParams,
      keyPair.value.privateKey,
      Codecs.Boolean.encode(body)
    )),
  }),
  verifyResult: async (signed: SignedBodyWrapper<Boolean>): Promise<boolean> =>
    crypto.subtle.verify(
      keyParams,
      publicKey: await crypto.subtle.importKey(keyFormat, signed.header.publicKey, keyParams, true, ['verify']),
      signed.header.signature,
      Codecs.Boolean.encode(signed.body)
    ),
});

// Send the pass request
const requestUserProfileResult = await requestUserProfile.sendRequest([
  'email',
  'profile.name',
  'profile.picture'
]);

if (requestUserProfileResult.status === 'accepted') {
  // If the user accepted the request and `requestUserProfile.sendRequest` did not throw, the result signature is valid
  const userProfileInfo = requestUserProfileResult.body; 
  const userPublicKey = requestUserProfileResult.signed.publicKey; 
  const validResultSignature = requestUserProfileResult.signed.signature; 
}

If the user approves this pass request, the application that made the request will receive a signature of the result and the user's public key.

Signature Info

In the example below, after you accept the pass request, you can click "Valid Request Signature" to show the result signature and the user's public key.

Interactive Example
This demo requests basic user profile info.
Click "Send Request"
The Pass Request will appear here