facebook / flow

Adds static typing to JavaScript to improve developer productivity and code quality.
https://flow.org/
MIT License
22.07k stars 1.85k forks source link

Add complete credential management definitions #7480

Open lgarron opened 5 years ago

lgarron commented 5 years ago

959a5292e8cee5f78f04d515f352ab176ddd37f0 added partial definitions for the credential management API at https://github.com/facebook/flow/blob/d294e2d82e44e2f306f282d1bbf43254ce8530ac/lib/bom.js#L1452-L1477

Unfortunately, these are hard to use for real-world webauthn code. I had been using the following local definitions, specifically tuned for PublicKeyCredential use:

// This is a subset of the Credential Management and WebAuthn APIs.
// File based on https://github.com/DefinitelyTyped/DefinitelyTyped/blob/f17caa9fa6d08a7d7b010c32568cab436a247516/types/webappsec-credential-management/index.d.ts
// Processed with https://github.com/joarwilk/flowgen and winnowed by hand to avoid fix-me comments in JS.

declare interface WebauthnNavigator extends Navigator {
  credentials?: CredentialsContainer;
}

// https://www.w3.org/TR/credential-management-1/#credentialscontainer
declare interface CredentialsContainer {
  get(options?: CredentialRequestOptionsWithPublicKey): Promise;
  create(options: CredentialCreationOptionsForPublicKey): Promise;
  preventSilentAccess(): Promise;
}

// https://www.w3.org/TR/2017/WD-credential-management-1-20170804/#enumdef-credentialmediationrequirement
declare type CredentialMediationRequirement =
  | "silent"
  | "optional"
  | "required";

// https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptions
declare interface PublicKeyCredentialRequestOptions {
  challenge: BufferSource;
  timeout: number;
  rpId?: string;
  allowCredentials?: PublicKeyCredentialDescriptor[];
  userVerification?: UserVerificationRequirement;
  extensions?: PublicKeyCredentialRequestOptionsExtensions;
}

// https://w3c.github.io/webauthn/#enumdef-publickeycredentialtype
declare type PublicKeyCredentialType = "public-key";

// https://w3c.github.io/webauthn/#enumdef-userverificationrequirement
declare type UserVerificationRequirement =
  | "required"
  | "preferred"
  | "discouraged";

declare interface CredentialRequestOptionsWithPublicKey {
  mediation?: CredentialMediationRequirement;
  publicKey: PublicKeyCredentialRequestOptions;
  // AbortSignal is not defined in Flow, so we comment this out for now.
  // signal?: AbortSignal;
}

// https://w3c.github.io/webauthn/#dictdef-publickeycredentialrpentity
declare interface PublicKeyCredentialRpEntity {
  id: string;
  name: string;
}

// https://w3c.github.io/webauthn/#dictdef-publickeycredentialuserentity
declare interface PublicKeyCredentialUserEntity {
  id: BufferSource;
  name: string;
  displayName: string;
}

// https://w3c.github.io/webauthn/#dictdef-publickeycredentialparameters
declare interface PublicKeyCredentialParameters {
  type: PublicKeyCredentialType;
  alg: number;
}

// https://w3c.github.io/webauthn/#transport}
declare type AuthenticatorTransport = "usb" | "nfc" | "ble" | "internal";

// https://w3c.github.io/webauthn/#dictdef-publickeycredentialdescriptor
declare interface PublicKeyCredentialDescriptor {
  type: PublicKeyCredentialType;
  id: BufferSource;
  transports?: AuthenticatorTransport[];
}

// https://w3c.github.io/webauthn/#attachment}
declare type AuthenticatorAttachment = "platform" | "cross-platform";

// https://w3c.github.io/webauthn/#dictdef-authenticatorselectioncriteria
declare interface AuthenticatorSelectionCriteria {
  authenticatorAttachment?: AuthenticatorAttachment;
  requireResidentKey?: boolean;
  requireUserVerification?: UserVerificationRequirement;
}

// https://w3c.github.io/webauthn/#attestation-convey}
declare type AttestationConveyancePreference = "none" | "indirect" | "direct";

declare interface CredentialCreationOptionsForPublicKey {
  publicKey?: PublicKeyCredentialCreationOptions;
  // AbortSignal is not defined in Flow, so we comment this out for now.
  // signal?: AbortSignal;
}

// https://w3c.github.io/webauthn/#dictdef-makepublickeycredentialoptions
declare interface PublicKeyCredentialCreationOptions {
  rp: PublicKeyCredentialRpEntity;
  user: PublicKeyCredentialUserEntity;
  challenge: BufferSource;
  pubKeyCredParams: PublicKeyCredentialParameters[];
  timeout?: number;
  excludeCredentials?: PublicKeyCredentialDescriptor[];
  authenticatorSelection?: AuthenticatorSelectionCriteria;
  attestation?: AttestationConveyancePreference;
  extensions?: any;
}

// https://w3c.github.io/webauthn/#authenticatorresponse
declare interface AuthenticatorResponse {
  clientDataJSON: ArrayBuffer;
}

// https://w3c.github.io/webauthn/#authenticatorattestationresponse
declare type AuthenticatorAttestationResponse = {
  attestationObject: ArrayBuffer
} & AuthenticatorResponse;

// https://w3c.github.io/webauthn/#iface-authenticatorassertionresponse
declare type AuthenticatorAssertionResponse = {
  authenticatorData: ArrayBuffer,
  signature: ArrayBuffer,
  userHandle: ArrayBuffer
} & AuthenticatorResponse;

// https://www.w3.org/TR/credential-management-1/#dictdef-credentialdata
declare interface CredentialData {
  id: string;
}

// https://w3c.github.io/webauthn/#publickeycredential
declare type PublicKeyCredential = {
  type: PublicKeyCredentialType,
  rawId: ArrayBuffer,
  response: AuthenticatorAttestationResponse | AuthenticatorAssertionResponse
} & CredentialData;

// Narrow types

declare type PublicKeyCredentialWithAssertion = {
  type: PublicKeyCredentialType,
  rawId: ArrayBuffer,
  response: AuthenticatorAssertionResponse
} & PublicKeyCredential;

declare type PublicKeyCredentialWithAttestation = {
  type: PublicKeyCredentialType,
  rawId: ArrayBuffer,
  response: AuthenticatorAttestationResponse
} & PublicKeyCredential;

declare type PublicKeyCredentialRequestOptionsExtensions = {
  appid?: string
}

However, it's difficult to cast from the Flow definitions to the ones needed for webauthn without going through any, which our project's linter doesn't accept.

TypeScript has a fairly complete definition at https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/webappsec-credential-management/index.d.ts (which is where I adapted my local defs from). I was considering contributing an adapted version to Flow, but 1) The TypeScript definition modifies fetch, which I didn't know how to adapt to Flow. 2) I was worried that a partial/partially battle-tested contribution would be missing details, and break someone else's use case.

Unfortunately, it looks like I find myself in that "someone else" category.

Would there be any concerns about adapting the TypeScript definitions wholesale, and/or is there someone who would be willing to help out or answer questions for how to do it?

lgarron commented 5 years ago

In case it's of any consequence, we just published https://github.com/github/webauthn-json without Flow typings. I'm working on adding Flow types to it, but they will probably have stubs with a lot of any unless we can reference correct credential management definitions built into Flow.