npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@govtechsg/ndi-app-check

v1.0.15

Published

NPM module to do app check for SPA

Downloads

1,314

Readme

NDI App Check

API Specs

1) NPM install @govtechsg/ndi-app-check

Import the ndi-app-check npm library

npm install ndi-app-check

2) Initialize the NdiAppCheck class

Import the ndi-app-check library into your codebase. ndi-app-check is am ESModule so it can only be used via import. However, CommonJS can still import the library via dynamic import syntax shown below.

Typescript (ESM)

import { 
    NdiAppCheck, // NdiAppCheck class houses the functions to do app check
    NdiAppCheckParams,  // NdiAppCheck.validateNdiAppCheckHeader function param interface
    Options // NdiAppCheck class constructor init param interface
} from "ndi-app-check"

import path from "path"
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// The file path to the Google service account json file
let serviceConfigFilePath = path.join(__dirname, "your-service-config.json")

// Alternatively use a json object instead of Google service account json config file
let serviceConfigJson = {
    "type": "service_account",
    "project_id": "your project_id",
    "private_key_id": "your private_key_id",
    "private_key": "<REDACTED>",
    "client_email": "play-integrity-verifier@your_project.iam.gserviceaccount.com",
    "client_id": "your client_id",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/play-integrity-verifier%40your_project.iam.gserviceaccount.com",
    "universe_domain": "googleapis.com"
}

let options: Options = {
    gcpServiceConfig: serviceConfigFilePath, // the file path string to your Google service account json file
    gcpServiceConfigJson: serviceConfigJson, // your service config json object, if this is supplied, gcpServiceConfig can be omitted
    iosDeviceCheckKey: "-----BEGIN PRIVATE KEY----- your key -----END PRIVATE KEY-----", // iOS device check private key to call apple deviec ehcek backend api
    iosDeviceCheckKeyId: "you device check key id", // iOS device check key id
    expiryLimitSeconds: 120, // optional expiry config, default is 120 seconds
    inProd: true,  // dictates the validation criteria between production and staging environment, allows package name sg.ndi.dev, and also disallow development environment for apple iOS attestation and deviceChecks
    debugMode: false  // // if true, more console logs will be printed to aid debugging
}

// Initialize NdiAppCheck class
let ndiAppCheck = new NdiAppCheck(options)

Javascript (CommonJS)

(async function() { // CommonJs does not allow top level await

    // The file path to the Google service account json file
    let serviceConfigFilePath = path.join(__dirname, "your-service-config.json")
    
    let options = {
        gcpServiceConfig: serviceConfigFilePath, // the file path string to your Google service account json file
        gcpServiceConfigJson: serviceConfigJson, // your service config json object, if this is supplied, gcpServiceConfig can be omitted
        iosDeviceCheckKey: "-----BEGIN PRIVATE KEY----- your key -----END PRIVATE KEY-----", // iOs device check private key to call apple deviec ehcek backend api
        iosDeviceCheckKeyId: "you device check key id", // iOS device check key id
        expiryLimitSeconds: 120, // optional expiry config, default is 120 seconds
        inProd: true,  // dictates the validation criteria between production and staging environment, allows package name sg.ndi.dev, and also disallow development environment for apple iOS attestation and deviceChecks
        debugMode: false  // if true, more console logs will be printed to aid debugging
    }

    let ndiAppCheck;
    await import("@govtechsg/ndi-app-check").then( ({ default: defaultImport }) => {
        // Initialize NdiAppCheck class
        ndiAppCheck = new defaultImport(options)
    } );
}) ();

2) Call the validateNdiAppCheckHeader function in NdiAppCheck class

NdiAppCheck class exposes 2 functions.

Typescript (ESM) validateNdiAppCheckHeader api call


// Determine if this header value requires a public key to validate
let headerNeedsPublicKey = ndiAppCheck.headerNeedsPublicKey(header)

// Fetch the public key from a database or other persistent storage
let pubkey = undefined
if (headerNeedPublicKey) {
    /**
     * fetch public keys from database
     */
    pubkey = <databased fetched publicKey>
}

let appcheck_params: NdiAppCheckParams = {
    header: header, //string - header value
    nonce: nonce, // string - nonce, hash, challenge (session-token, reg_ref, swk_qr_ref etc)
    publicKey: pubKey, // any - publickey only used for Apple key attestation, this value should be obtained from the result of validateNdiAppCheckHeader when a publickey is returned
}

let ndiAppCheckResult = await ndiAppCheck.validateNdiAppCheckHeader(appcheck_params)

Javascript (CommonJS) validateNdiAppCheckHeader api call


// Determine if this header value requires a public key to validate
let headerNeedsPublicKey = ndiAppCheck.headerNeedsPublicKey(header)

// Fetch the public key from a database or other persistent storage
let pubkey = undefined
if (headerNeedPublicKey) {
    /**
     * fetch public keys from database
     */
    pubkey = <databased fetched publicKey>
}

let appcheck_params = {
    header: header, //string - header value
    nonce: nonce, // string - nonce, hash, challenge (session-token, reg_ref, swk_qr_ref etc)
    publicKey: pubKey, // any - publickey only used for Apple key attestation, this value should be obtained from the result of validateNdiAppCheckHeader when a publickey is returned
}

let ndiAppCheckResult = await ndiAppCheck.validateNdiAppCheckHeader(appcheck_params)

Typescript (ESM) validateNdiAppCheckHeader response format

// result returned from ndiAppCheck.validateNdiAppCheckHeader conforms to the below interface
declare interface NdiAppCheckResponse {
    state: Boolean; // state will be true when validation was successful
    reason?: string; // reason will NOT be undefined/null when state == false and validation failed due to security criteria
    error?: string; // error will NOT be undefined/null when state == false and validation failed with an exception/error
    pubicKey?: any; // pubkey will NOT be undefined/null when state === true and validation succeeded with apple iOS attestation header
}

The NdiAppCheck.validateNdiAppCheckHeader function will return a result adhering to the interface NdiAppCheckResponse.

if ndiAppCheckResult contains a publicKey property, it means the validateNdiAppCheckHeader call was made using a Apple iOS key attestation header. This publicKey is to be saved into a database so that when a validateNdiAppCheckHeader call made with Apple iOS app assertion header, the assertion header can be validated successfully using the aforementioned publicKey

Accepted Header formats

Android (Google Play)

Header for Google Play Integrity

X-ndi-appcheck: gpi <token>

Android (Huawei)

Header for Huawei SysIntegrity

X-ndi-appcheck: hwsi <token>

Apple iOS

Header for Apple iOS DeviceCheck

X-ndi-appcheck: adc <token>

Header for Apple iOS App Key Attestation

X-ndi-appcheck: aka <token>

Header for Apple iOS App Assertion

X-ndi-appcheck: aaa <token>

Contributing

  1. Git clone the project from private repository (do contact the author for permissions)
  2. cd SpaAppAttestationModule
  3. Run npm i
  4. As an optional step (if the node-app-attest library has not been transpiled i.e. node-app-attest folder does not exist on the root of this project directory), run
npm run babel:node-app-attest
  • Afterwhich, search for the following usages of the asn1js and pkijs library in node-app-attest/src/verifyAttestation.js
    const asn1 = _asn1js.default.fromBER(clientCertificate.raw);
    const certificate = new _pkijs.default.Certificate({
    schema: asn1.result
    });
    and remove the default property from _asn1js and _pkijs. This will result in the following.
    const asn1 = _asn1js.fromBER(clientCertificate.raw);
    const certificate = new _pkijs.Certificate({
    schema: asn1.result
    });
  1. Build the src folder by running npm run build
  2. npm publish for publishing the npm package