Skip to content

Latest commit

 

History

History
328 lines (264 loc) · 8.88 KB

README.md

File metadata and controls

328 lines (264 loc) · 8.88 KB

Passkey API

The Passkey API is designed for frontend developers to easily implement and test passkey authentication using the SimpleWebAuthn library. It’s a quick solution for trying out passkey auth with reliable APIs but is not intended for production use.

How does it work

Under the hood this project uses @simplewebauthn/server package. For implementation on the frontend client sdk is available from the same package.

Dependencies

How to run

Step 1: Clone the repository

git clone https://github.com/yalturkey/passkey-api.git

Step 2: Install dependencies

npm install --legacy-peer-deps

Step 3: Run the application

npm run start

Runs on port 2000.

API Definition

What is RP_ID?

RP_ID is a short for 'relying party id', it is basically root level FQDN for which the passkey is registered. It is required at the time of challenge generation (for registration & signup) and verification. The RP_ID must match the root frontend domain name from which the passkey requests are originating. For example with RP_ID set as 'acme.com', frontends hosted on any subdomains '*.acme.com' can be used to initiate passkeys.

What is CLIENT_URL?

CLIENT_URL is the full frontend url from which the passkey requests are initiated (including https://).

Postman Collection

Passkey API.postman_collection.json

How to use (Frontend integration guide)

Step 1: Register a new user email

POST Request Endpoint

POST /api/auth/register?RP_ID=localhost

POST Request Body

{
    "email": "[email protected]"
}

POST Request Response

{
    "success": true,
    "status": 200,
    "data": {
        "challenge": "Bkp7E3xuVEBe8bc-7X2-oF_oDwkU_DHK70PKRu5ubEo",
        "rp": {
            "name": "acme",
            "id": "localhost"
        },
        "user": {
            "id": "aif-X2DcI0pMpkTrvReYj5622eV8B28kQDs-oAq0qH8",
            "name": "[email protected]",
            "displayName": ""
        },
        "pubKeyCredParams": [
            {
                "alg": -8,
                "type": "public-key"
            },
            {
                "alg": -7,
                "type": "public-key"
            },
            {
                "alg": -257,
                "type": "public-key"
            }
        ],
        "timeout": 60000,
        "attestation": "none",
        "excludeCredentials": [],
        "authenticatorSelection": {
            "residentKey": "preferred",
            "userVerification": "preferred",
            "requireResidentKey": false
        },
        "extensions": {
            "credProps": true
        }
    }
}

Step 2: Pass the response data to the @simplewebauthn/browser client sdk

import { startRegistration } from '@simplewebauthn/browser'

const optionsJSON = {
  challenge: 'Bkp7E3xuVEBe8bc-7X2-oF_oDwkU_DHK70PKRu5ubEo',
  rp: {
    name: 'acme',
    id: 'localhost',
  },
  user: {
    id: 'aif-X2DcI0pMpkTrvReYj5622eV8B28kQDs-oAq0qH8',
    name: '[email protected]',
    displayName: '',
  },
  pubKeyCredParams: [
    {
      alg: -8,
      type: 'public-key',
    },
    {
      alg: -7,
      type: 'public-key',
    },
    {
      alg: -257,
      type: 'public-key',
    },
  ],
  timeout: 60000,
  attestation: 'none',
  excludeCredentials: [],
  authenticatorSelection: {
    residentKey: 'preferred',
    userVerification: 'preferred',
    requireResidentKey: false,
  },
  extensions: {
    credProps: true,
  },
}

await startRegistration({ optionsJSON })

Step 3: Verify the passkey with the server

POST Request Endpoint

POST /api/auth/register/verify?RP_ID=localhost&CLIENT_URL=http://localhost:9000

POST Request Body

{
    "email": "[email protected]",
    "userId": "1093444839",
    "challenge": "IOHpI3SCo6OnBOA_Zyj0jV9VXC5pS2qdle46Bsu253A",
    "id": "bkWG-cWnltO7jjm1qgvx_3QCHvKj8jyno-grTi-4OEY",
    "rawId": "bkWG-cWnltO7jjm1qgvx_3QCHvKj8jyno-grTi-4OEY",
    "response": {
        "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAK3OAAI1vMYKZIsLJfHwVQMAIG5FhvnFp5bTu445taoL8f90Ah7yo_I8p6PoK04vuDhGpQECAyYgASFYIAQKWaLi0pPQTEH7WDj5l5FHgBrK7yFnI9U_Fadtc0i8IlggdmF0zzcjy9R6iSS2fFpk51DcxgRSkxOeRFRuyvGE8Wo",
        "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSU9IcEkzU0NvNk9uQk9BX1p5ajBqVjlWWEM1cFMycWRsZTQ2QnN1MjUzQSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
        "transports": [
            "internal"
        ],
        "publicKeyAlgorithm": -7,
        "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBApZouLSk9BMQftYOPmXkUeAGsrvIWcj1T8Vp21zSLx2YXTPNyPL1HqJJLZ8WmTnUNzGBFKTE55EVG7K8YTxag",
        "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAK3OAAI1vMYKZIsLJfHwVQMAIG5FhvnFp5bTu445taoL8f90Ah7yo_I8p6PoK04vuDhGpQECAyYgASFYIAQKWaLi0pPQTEH7WDj5l5FHgBrK7yFnI9U_Fadtc0i8IlggdmF0zzcjy9R6iSS2fFpk51DcxgRSkxOeRFRuyvGE8Wo"
    },
    "type": "public-key",
    "clientExtensionResults": {
        "credProps": {
            "rk": true
        }
    },
    "authenticatorAttachment": "platform"
}

POST Request Response

{
    "success": true,
    "status": 200,
    "verified": true
}

Step 4: Request login with the passkey from the server

POST Request Endpoint

POST /api/auth/login?RP_ID=localhost

POST Request Body

{
    "email": "[email protected]"
}

POST Request Response

{
    "success": true,
    "status": 200,
    "data": {
        "rpId": "localhost",
        "challenge": "R5g601iRnobEVaY-I2j7VwgkDzcr1cbiVD3dsp11DYk",
        "allowCredentials": [
            {
                "id": "bkWG-cWnltO7jjm1qgvx_3QCHvKj8jyno-grTi-4OEY",
                "type": "public-key"
            }
        ],
        "timeout": 60000,
        "userVerification": "preferred"
    }
}

Step 5: Pass the response data to the @simplewebauthn/browser client sdk

import { startAuthentication } from '@simplewebauthn/browser'

const optionsJSON = {
  rpId: 'localhost',
  challenge: 'R5g601iRnobEVaY-I2j7VwgkDzcr1cbiVD3dsp11DYk',
  allowCredentials: [
    {
      id: 'bkWG-cWnltO7jjm1qgvx_3QCHvKj8jyno-grTi-4OEY',
      type: 'public-key',
    },
  ],
  timeout: 60000,
  userVerification: 'preferred',
}

await startAuthentication({ optionsJSON })

Step 6: Verify the passkey with the server and get the auth tokens

POST Request Endpoint

POST /api/auth/login/verify?RP_ID=localhost&CLIENT_URL=http://localhost:9000

POST Request Body

{
    "email": "[email protected]",
    "challenge": "gKVUXDLxR9ev-KTDOWTlQb0cr57WT5HhC7DWkNWFakk",
    "response": {
        "id": "bkWG-cWnltO7jjm1qgvx_3QCHvKj8jyno-grTi-4OEY",
        "rawId": "bkWG-cWnltO7jjm1qgvx_3QCHvKj8jyno-grTi-4OEY",
        "response": {
            "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAA",
            "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZ0tWVVhETHhSOWV2LUtURE9XVGxRYjBjcjU3V1Q1SGhDN0RXa05XRmFrayIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
            "signature": "MEQCIGZUx4F3l_wq3Pfad1arrPvJ65EpKv_WEnm3dWX2EArlAiBORz44aVuy1vxRObv3L4qJTkWccxjunkSvdkAJYrdvVw",
            "userHandle": "_fWJ_BkgyCWfHq57LDr0QpzvP-ZpCTISiE8z7mzx_tA"
        },
        "type": "public-key",
        "clientExtensionResults": {},
        "authenticatorAttachment": "platform"
    }
}

POST Request Response

{
    "success": true,
    "status": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjo0NTE3OTU3MzcsInZlcmlmaWVkIjpmYWxzZSwic291cmNlIjoiZGFzaGJvYXJkIiwiZmluZ2VycHJpbnQiOiI0OGFiMzYxY2NlZWIyZWY2NWIxYjBiYmRhNWY2ZmYzNCIsImVtYWlsIjoiZW1haWxAZXhhbXBsZS5jb20iLCJpYXQiOjE3MzY2MjQwNDksImV4cCI6MTczNjYyNzY0OX0.WhUwsUMzHUp5nNL2qVhkT07yi3z5l1sMnkYUSeROApw",
        "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjo0NTE3OTU3MzcsInZlcmlmaWVkIjpmYWxzZSwic291cmNlIjoiZGFzaGJvYXJkIiwiZmluZ2VycHJpbnQiOiI0OGFiMzYxY2NlZWIyZWY2NWIxYjBiYmRhNWY2ZmYzNCIsImVtYWlsIjoiZW1haWxAZXhhbXBsZS5jb20iLCJpYXQiOjE3MzY2MjQwNDksImV4cCI6MTczNzIyODg0OX0.sEocXcWvgzQ9rcSN5WiAu7dDIZVVNcQvlYxFWzXEwpI",
        "expires_at": "2025-01-25T19:34:09.983Z"
    }
}

OPTIONAL Step 7: Verify all users data

GET Request Endpoint

GET /api/auth/users

OPTIONAL Step 8: Purge all users data

DELETE Request Endpoint

DELETE /api/auth/users/purge