-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
369 additions
and
346 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import loginResponseEndpoint from './loginResponseEndpoint' | ||
import loginChallengeEndpoint from './loginChallengeEndpoint' | ||
import multiAuthLoginEndpoint from './multiAuthLoginEndpoint' | ||
import multiAuthPreEndpoint from './multiAuthPreEndpoint' | ||
import registerChallengeEndpoint from './registerChallengeEndpoint' | ||
import registerResponseEndpoint from './registerResponseEndpoint' | ||
|
||
export { | ||
loginResponseEndpoint, | ||
loginChallengeEndpoint, | ||
multiAuthLoginEndpoint, | ||
multiAuthPreEndpoint, | ||
registerChallengeEndpoint, | ||
registerResponseEndpoint, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import type { Endpoint } from 'payload/config' | ||
import { generateAuthenticationOptions } from '@simplewebauthn/server' | ||
import { getUserByEmail, getUserPasskeys } from '../utils' | ||
import pluginConfig from '../pluginConfig' | ||
|
||
const loginChallengeEndpoint: Endpoint = { | ||
path: '/multi-auth/auth/challenge', | ||
method: 'post', | ||
root: true, | ||
handler: async (req, res) => { | ||
if (!req.body || !req.body.email) { | ||
return res.status(400).json({ message: 'Email missing' }) | ||
} | ||
|
||
const user = await getUserByEmail(req.body.email, req.payload, pluginConfig.authCollectionSlug) | ||
const userPasskeys = await getUserPasskeys(user, req.payload) | ||
|
||
const options = await generateAuthenticationOptions({ | ||
rpID: 'localhost', // Use your actual domain in production | ||
allowCredentials: userPasskeys.map((passkey: { credentialID: string }) => ({ | ||
id: passkey.credentialID, | ||
type: 'public-key', | ||
transports: ['usb', 'nfc', 'ble', 'internal'], | ||
challenge: passkey.challenge, | ||
})), | ||
userVerification: 'preferred', | ||
}) | ||
|
||
console.log(options) | ||
|
||
await req.payload.update({ | ||
collection: pluginConfig.authCollectionSlug, | ||
id: user.id, | ||
data: { options } as any, | ||
}) | ||
res.json(options) | ||
|
||
return res | ||
}, | ||
} | ||
|
||
export default loginChallengeEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import type { Endpoint } from 'payload/config' | ||
import { randomBytes } from 'crypto' | ||
import { getUserByEmail, getUserPasskeys } from '../utils' | ||
import pluginConfig from '../pluginConfig' | ||
|
||
const loginResponseEndpoint: Endpoint = { | ||
path: '/multi-auth/auth/verify', | ||
method: 'post', | ||
root: true, | ||
handler: async (req, res) => { | ||
const { body } = req | ||
|
||
if (!body) { | ||
return res.status(400).json({ message: 'Malformed request' }); | ||
} | ||
|
||
const user = await getUserByEmail(body.email, req.payload, pluginConfig.authCollectionSlug) | ||
const passkeys = await getUserPasskeys(user, req.payload) | ||
|
||
const passkey = passkeys.find( | ||
(existingPasskey: { credentialID: any }) => existingPasskey.credentialID === body.id, | ||
) | ||
|
||
if (!passkey) { | ||
return res.status(401).json({ message: 'Invalid credentials' }) | ||
} | ||
|
||
// let verification; | ||
// try { | ||
// verification = await verifyAuthenticationResponse({ | ||
// response: body, | ||
// expectedChallenge: user.registrationOptions.challenge, | ||
// expectedOrigin: 'http://localhost:3000', | ||
// expectedRPID: 'localhost', | ||
// authenticator: { | ||
// credentialID: passkey.credentialID, | ||
// credentialPublicKey: passkey.publicKey, | ||
// counter: passkey.counter, | ||
// transports: passkey.transports, | ||
// }, | ||
// }); | ||
// } catch (error) { | ||
// console.error(error); | ||
// return res.status(400).send({ error: error.message }); | ||
// } | ||
|
||
// const { verified } = verification; | ||
|
||
const password = randomBytes(32).toString('hex') | ||
|
||
// await req.payload.update({ | ||
// collection: pluginConfig.authCollectionSlug, | ||
// id: user.id, | ||
// data: {password} as any, | ||
// }) | ||
|
||
const loginData = { | ||
collection: pluginConfig.authCollectionSlug, | ||
data: { | ||
email: user.email, | ||
password: 'Caconmi08!', | ||
}, | ||
req, | ||
} | ||
console.log(loginData) | ||
const result = await req.payload.login(loginData) | ||
|
||
const data = { success: true, data: result } | ||
console.log(data) | ||
return res.json(data) | ||
}, | ||
} | ||
|
||
export default loginResponseEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import type { Endpoint } from 'payload/config' | ||
import { getUserByEmail } from '../utils' | ||
import pluginConfig from '../pluginConfig' | ||
|
||
const multiAuthLoginEndpoint: Endpoint = { | ||
path: '/multi-auth/auth/login', | ||
method: 'post', | ||
root: true, | ||
handler: async (req, res) => { | ||
// secure this request | ||
if (!req.body) { | ||
return res.status(400).json({ message: 'Malformed request' }) | ||
} | ||
|
||
const { email, password } = req.body | ||
const user = await getUserByEmail(email, req.payload, pluginConfig.authCollectionSlug) | ||
|
||
if (!user) { | ||
return res.status(404).json({ message: 'User not found' }) | ||
} | ||
|
||
const loginData = { | ||
collection: pluginConfig.authCollectionSlug, | ||
data: { | ||
email, | ||
password, | ||
}, | ||
req, | ||
} | ||
|
||
let result | ||
try { | ||
result = await req.payload.login(loginData) | ||
} catch (error: unknown) { | ||
console.log(error.message) | ||
return res.status(401).json({ message: error.message }) | ||
} | ||
|
||
if (user.authMethod === 'password_2fa' && user.tokenValidation === false) { | ||
return res.status(401).json({ message: 'Invalid token' }) | ||
} | ||
|
||
const data = { success: true, data: result } | ||
return res.json(data) | ||
}, | ||
} | ||
|
||
export default multiAuthLoginEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { Endpoint } from 'payload/config' | ||
import { getUserByEmail } from '../utils' | ||
import pluginConfig from '../pluginConfig' | ||
|
||
const multiAuthPreEndpoint: Endpoint = { | ||
path: '/multi-auth/auth/pre', | ||
method: 'post', | ||
root: true, | ||
handler: async (req, res) => { | ||
// secure this request | ||
if (!req.body) { | ||
return res.status(400).json({message: 'Malformed request'}) | ||
} | ||
|
||
const { email } = req.body | ||
|
||
const user = await getUserByEmail(email, req.payload, pluginConfig.authCollectionSlug) | ||
|
||
if (!user) { | ||
return res.status(404).json({ message: 'User not found' }); | ||
} | ||
|
||
// return user preferred authentication method | ||
return res.json({ authMethod: user.authMethod || 'password' }) | ||
}, | ||
} | ||
|
||
export default multiAuthPreEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import type { Endpoint } from 'payload/config' | ||
import { generateRegistrationOptions } from '@simplewebauthn/server' | ||
import { getUserByEmail, getUserPasskeys } from '../utils' | ||
import pluginConfig from '../pluginConfig' | ||
|
||
const registerChallengeEndpoint: Endpoint = { | ||
path: '/multi-auth/register/challenge', | ||
method: 'post', | ||
root: true, | ||
handler: async (req, res) => { | ||
const body = req.body | ||
|
||
if (!body.email) { | ||
return res.status(400).json({ message: 'Email is required' }) | ||
} | ||
|
||
try { | ||
const user = await getUserByEmail(body.email, req.payload, pluginConfig.authCollectionSlug) | ||
|
||
const userPasskeys = await getUserPasskeys(user, req.payload) | ||
const rpName = pluginConfig.rpName | ||
const rpID = pluginConfig.rpID | ||
|
||
// Generate registration options | ||
const options = await generateRegistrationOptions({ | ||
rpName, | ||
rpID, | ||
userName: user.email, | ||
attestationType: 'none', | ||
excludeCredentials: userPasskeys.map((passkey: { credentialID: any; transports: any; }) => ({ | ||
id: passkey.credentialID, | ||
})), | ||
authenticatorSelection: { | ||
residentKey: 'preferred', | ||
userVerification: 'preferred', | ||
authenticatorAttachment: 'platform', | ||
}, | ||
}) | ||
|
||
// save the options to the user | ||
await req.payload.update({ | ||
collection: pluginConfig.authCollectionSlug, | ||
id: user.id, | ||
data: { | ||
registrationOptions: options, | ||
}, | ||
}) | ||
|
||
// Send the options back to the client | ||
res.json(options) | ||
} catch (error: unknown) { | ||
console.error('Error processing request:', error) | ||
res.status(500).send('Internal Server Error') | ||
} | ||
}, | ||
} | ||
|
||
export default registerChallengeEndpoint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import type { Endpoint } from 'payload/config' | ||
import type { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/types' | ||
import { verifyRegistrationResponse } from '@simplewebauthn/server' | ||
import type Passkey from '../types/Passkey' | ||
import pluginConfig from '../pluginConfig' | ||
import {getUserByEmail} from "../utils"; | ||
|
||
const registerResponseEndpoint: Endpoint = { | ||
path: '/multi-auth/register/verify', | ||
method: 'post', | ||
root: true, | ||
handler: async (req, res) => { | ||
const body = req.body | ||
const user = await getUserByEmail(body.email, req.payload, pluginConfig.authCollectionSlug) | ||
|
||
console.log("verify") | ||
// @ts-ignore | ||
const currentOptions: PublicKeyCredentialCreationOptionsJSON = user.registrationOptions | ||
|
||
let verification | ||
try { | ||
verification = await verifyRegistrationResponse({ | ||
response: body, | ||
expectedChallenge: currentOptions.challenge, | ||
expectedOrigin: 'http://localhost:3000', | ||
expectedRPID: 'localhost', | ||
}) | ||
} catch (error: unknown) { | ||
console.error(error) | ||
return res.status(400).send({ error: error.message }) | ||
} | ||
|
||
const { verified } = verification | ||
const { registrationInfo } = verification | ||
const { credentialID, credentialPublicKey, counter, credentialDeviceType, credentialBackedUp } = | ||
registrationInfo | ||
|
||
const newPasskey: Passkey = { | ||
user: user.id, | ||
webAuthnUserID: currentOptions.user.id, | ||
credentialID: credentialID, | ||
publicKey: credentialPublicKey, | ||
counter, | ||
deviceType: credentialDeviceType, | ||
backedUp: credentialBackedUp, | ||
transports: body.response.transports.join(','), | ||
} | ||
|
||
// Save the new passkey to the database | ||
const payload = await req.payload.create({ | ||
collection: 'authenticators', | ||
data: newPasskey, | ||
}) | ||
|
||
return res.json({ verified }) | ||
}, | ||
} | ||
|
||
export default registerResponseEndpoint |
Oops, something went wrong.