Skip to content

Commit

Permalink
feat: added passkey and webauthn support
Browse files Browse the repository at this point in the history
  • Loading branch information
titanism committed Dec 13, 2023
1 parent 3303872 commit b32a46d
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 17 deletions.
74 changes: 68 additions & 6 deletions index.js
@@ -1,17 +1,22 @@
const crypto = require('crypto');
const fs = require('fs');
const process = require('process');
const crypto = require('node:crypto');
const fs = require('node:fs');
const process = require('node:process');

const AppleStrategy = require('@nicokaiser/passport-apple').Strategy;
const GitHubStrategy = require('passport-github2').Strategy;
const GoogleStrategy = require('passport-google-oauth20');
const OtpStrategy = require('@ladjs/passport-otp-strategy').Strategy;
const WebAuthnStrategy = require('passport-fido2-webauthn');
const _ = require('lodash');
const isSANB = require('is-string-and-not-blank');
const validator = require('validator');
const { KoaPassport } = require('koa-passport');
const { boolean } = require('boolean');

const { SessionChallengeStore } = WebAuthnStrategy;

const store = new SessionChallengeStore();

const PASSPORT_FIELDS = {
lastLoginField: 'last_login_at',

Expand Down Expand Up @@ -47,7 +52,8 @@ const PASSPORT_PHRASES = {
CONSENT_REQUIRED:
'Offline access consent required to generate a new refresh token.',
OTP_NOT_ENABLED: 'OTP authentication is not enabled.',
OTP_TOKEN_DOES_NOT_EXIST: 'OTP token does not exist for validation.'
OTP_TOKEN_DOES_NOT_EXIST: 'OTP token does not exist for validation.',
INVALID_WEBAUTHN_KEY: 'Invalid WebAuthn key.'
};

class Passport extends KoaPassport {
Expand All @@ -64,7 +70,8 @@ class Passport extends KoaPassport {
apple: boolean(process.env.AUTH_APPLE_ENABLED),
google: boolean(process.env.AUTH_GOOGLE_ENABLED),
github: boolean(process.env.AUTH_GITHUB_ENABLED),
otp: boolean(process.env.AUTH_OTP_ENABLED)
otp: boolean(process.env.AUTH_OTP_ENABLED),
webauthn: boolean(process.env.AUTH_WEBAUTHN_ENABLED)
},
strategies: {
apple: {
Expand Down Expand Up @@ -102,6 +109,11 @@ class Passport extends KoaPassport {
// allow last and current totp passcode
window: 1
}
},
webauthn: {
store
// <https://github.com/jaredhanson/passport-webauthn/pull/4>
// passReqToCallback: true
}
},
fields: PASSPORT_FIELDS,
Expand All @@ -118,7 +130,7 @@ class Passport extends KoaPassport {
if (err) return done(err);
// if no user exists then invalidate the previous session
// <https://github.com/jaredhanson/passport/issues/6#issuecomment-4857287>
done(null, user ? user : false);
done(null, user || false);
});
});

Expand Down Expand Up @@ -156,6 +168,56 @@ class Passport extends KoaPassport {
this.loginOrCreateProfile(Users, 'google')
)
);

// webauthn
if (this.config.providers.webauthn)
this.use(
new WebAuthnStrategy(
this.config.strategies.webauthn,
(credentialId, userHandle, done) => {
Users.findOne(
{
'passkeys.credentialId': credentialId
},
(err, user) => {
if (err) return done(err);
if (!user)
return done(
new Error(this.config.phrases.INVALID_WEBAUTHN_KEY)
);
if (
!Array.isArray(user.passkeys) ||
user.passkeys.length === 0
)
return done(
new Error(this.config.phrases.INVALID_WEBAUTHN_KEY)
);
const match = user.passkeys.find(
(p) => p.credentialId === credentialId
);
if (!match)
return done(
new Error(this.config.phrases.INVALID_WEBAUTHN_KEY)
);
// if (Buffer.compare(Buffer.from(user.id), userHandle) !== 0)
if (user.id !== userHandle.toString('base64'))
return done(
new Error(this.config.phrases.INVALID_WEBAUTHN_KEY)
);
done(null, user.toObject(), match.publicKey);
}
);
},
//
// NOTE: note we don't allow creation without a user
// (unlike passport-fido2-webauthn default example)
//
(user, id, publicKey, done) => {
return done(new Error(this.config.phrases.INVALID_WEBAUTHN_KEY));
// done(null, user.toObject());
}
)
);
}

// otp
Expand Down
23 changes: 12 additions & 11 deletions package.json
Expand Up @@ -19,27 +19,28 @@
"is-string-and-not-blank": "^0.0.2",
"koa-passport": "^4.1.4",
"lodash": "^4.17.21",
"passport-fido2-webauthn": "^0.1.0",
"passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0",
"validator": "^13.7.0"
"validator": "^13.11.0"
},
"devDependencies": {
"@commitlint/cli": "^17.0.2",
"@commitlint/config-conventional": "^17.0.2",
"ava": "^4.3.0",
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"ava": "4",
"cross-env": "^7.0.3",
"eslint": "^8.17.0",
"eslint": "8.39.0",
"eslint-config-xo-lass": "^2.0.1",
"fixpack": "^4.0.0",
"husky": "^8.0.1",
"lint-staged": "^13.0.0",
"mongodb-memory-server": "^8.6.0",
"mongoose": "^6.3.5",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"mongodb-memory-server": "^9.1.3",
"mongoose": "6",
"nyc": "^15.1.0",
"passport-local": "^1.0.0",
"remark-cli": "^10.0.1",
"remark-cli": "11.0.0",
"remark-preset-github": "^4.0.4",
"xo": "^0.49.0"
"xo": "0.53.1"
},
"engines": {
"node": ">=14"
Expand Down

0 comments on commit b32a46d

Please sign in to comment.