feat: implement hardware key auth
This commit is contained in:
parent
0035684040
commit
f987a07e0b
17 changed files with 1302 additions and 36 deletions
165
lib/new-admin/graphql/modules/authentication/FIDO2FAStrategy.js
Normal file
165
lib/new-admin/graphql/modules/authentication/FIDO2FAStrategy.js
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
const simpleWebauthn = require('@simplewebauthn/server')
|
||||
const base64url = require('base64url')
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const userManagement = require('../userManagement')
|
||||
const credentials = require('../../../../hardware-credentials')
|
||||
const T = require('../../../../time')
|
||||
const users = require('../../../../users')
|
||||
|
||||
const REMEMBER_ME_AGE = 90 * T.day
|
||||
|
||||
const rpID = `localhost`
|
||||
const expectedOrigin = `https://${rpID}:3001`
|
||||
|
||||
const generateAttestationOptions = (userID, session) => {
|
||||
return users.getUserById(userID).then(user => {
|
||||
return Promise.all([credentials.getHardwareCredentialsOfUser(user.id), user])
|
||||
}).then(([userDevices, user]) => {
|
||||
const options = simpleWebauthn.generateAttestationOptions({
|
||||
rpName: 'Lamassu',
|
||||
rpID,
|
||||
userName: user.username,
|
||||
userID: user.id,
|
||||
timeout: 60000,
|
||||
attestationType: 'indirect',
|
||||
excludeCredentials: userDevices.map(dev => ({
|
||||
id: dev.data.credentialID,
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
||||
})),
|
||||
authenticatorSelection: {
|
||||
userVerification: 'discouraged',
|
||||
requireResidentKey: false
|
||||
}
|
||||
})
|
||||
|
||||
session.webauthn = {
|
||||
attestation: {
|
||||
challenge: options.challenge
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
}
|
||||
|
||||
const generateAssertionOptions = (username, password, context) => {
|
||||
return userManagement.authenticateUser(username, password).then(user => {
|
||||
return credentials.getHardwareCredentialsOfUser(user.id).then(devices => {
|
||||
const options = simpleWebauthn.generateAssertionOptions({
|
||||
timeout: 60000,
|
||||
allowCredentials: devices.map(dev => ({
|
||||
id: dev.data.credentialID,
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
||||
})),
|
||||
userVerification: 'discouraged',
|
||||
rpID
|
||||
})
|
||||
|
||||
context.req.session.webauthn = {
|
||||
assertion: {
|
||||
challenge: options.challenge
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const validateAttestation = (userID, attestationResponse, context) => {
|
||||
const webauthnData = context.req.session.webauthn.attestation
|
||||
const expectedChallenge = webauthnData.challenge
|
||||
|
||||
return users.getUserById(userID).then(user => {
|
||||
return simpleWebauthn.verifyAttestationResponse({
|
||||
credential: attestationResponse,
|
||||
expectedChallenge: `${expectedChallenge}`,
|
||||
expectedOrigin,
|
||||
expectedRPID: rpID
|
||||
}).then(async verification => {
|
||||
const { verified, attestationInfo } = verification
|
||||
|
||||
if (verified && attestationInfo) {
|
||||
const {
|
||||
counter,
|
||||
credentialPublicKey,
|
||||
credentialID
|
||||
} = attestationInfo
|
||||
|
||||
const userDevices = await credentials.getHardwareCredentialsOfUser(user.id)
|
||||
const existingDevice = userDevices.find(device => device.data.credentialID === credentialID)
|
||||
|
||||
if (!existingDevice) {
|
||||
const newDevice = {
|
||||
counter,
|
||||
credentialPublicKey,
|
||||
credentialID
|
||||
}
|
||||
credentials.createHardwareCredential(user.id, newDevice)
|
||||
}
|
||||
}
|
||||
|
||||
context.req.session.webauthn = null
|
||||
return verified
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const validateAssertion = (username, password, rememberMe, assertionResponse, context) => {
|
||||
return userManagement.authenticateUser(username, password).then(user => {
|
||||
const expectedChallenge = context.req.session.webauthn.assertion.challenge
|
||||
|
||||
return credentials.getHardwareCredentialsOfUser(user.id).then(async devices => {
|
||||
const dbAuthenticator = _.find(dev => {
|
||||
return Buffer.from(dev.data.credentialID).compare(base64url.toBuffer(assertionResponse.rawId)) === 0
|
||||
}, devices)
|
||||
|
||||
if (!dbAuthenticator.data) {
|
||||
throw new Error(`Could not find authenticator matching ${assertionResponse.id}`)
|
||||
}
|
||||
|
||||
const convertedAuthenticator = _.merge(
|
||||
dbAuthenticator.data,
|
||||
{ credentialPublicKey: Buffer.from(dbAuthenticator.data.credentialPublicKey) }
|
||||
)
|
||||
|
||||
let verification
|
||||
try {
|
||||
verification = simpleWebauthn.verifyAssertionResponse({
|
||||
credential: assertionResponse,
|
||||
expectedChallenge: `${expectedChallenge}`,
|
||||
expectedOrigin,
|
||||
expectedRPID: rpID,
|
||||
authenticator: convertedAuthenticator
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
const { verified, assertionInfo } = verification
|
||||
|
||||
if (verified) {
|
||||
dbAuthenticator.data.counter = assertionInfo.newCounter
|
||||
await credentials.updateHardwareCredential(dbAuthenticator)
|
||||
|
||||
const finalUser = { id: user.id, username: user.username, role: user.role }
|
||||
context.req.session.user = finalUser
|
||||
if (rememberMe) context.req.session.cookie.maxAge = REMEMBER_ME_AGE
|
||||
}
|
||||
|
||||
context.req.session.webauthn = null
|
||||
return verified
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateAttestationOptions,
|
||||
generateAssertionOptions,
|
||||
validateAttestation,
|
||||
validateAssertion
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
const simpleWebauthn = require('@simplewebauthn/server')
|
||||
const base64url = require('base64url')
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const credentials = require('../../../../hardware-credentials')
|
||||
const T = require('../../../../time')
|
||||
const users = require('../../../../users')
|
||||
|
||||
const REMEMBER_ME_AGE = 90 * T.day
|
||||
|
||||
const rpID = `localhost`
|
||||
const expectedOrigin = `https://${rpID}:3001`
|
||||
|
||||
const generateAttestationOptions = (userID, session) => {
|
||||
return users.getUserById(userID).then(user => {
|
||||
return Promise.all([credentials.getHardwareCredentialsOfUser(user.id), user])
|
||||
}).then(([userDevices, user]) => {
|
||||
const options = simpleWebauthn.generateAttestationOptions({
|
||||
rpName: 'Lamassu',
|
||||
rpID,
|
||||
userName: user.username,
|
||||
userID: user.id,
|
||||
timeout: 60000,
|
||||
attestationType: 'indirect',
|
||||
excludeCredentials: userDevices.map(dev => ({
|
||||
id: dev.data.credentialID,
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
||||
})),
|
||||
authenticatorSelection: {
|
||||
userVerification: 'discouraged',
|
||||
requireResidentKey: false
|
||||
}
|
||||
})
|
||||
|
||||
session.webauthn = {
|
||||
attestation: {
|
||||
challenge: options.challenge
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
}
|
||||
|
||||
const generateAssertionOptions = (username, context) => {
|
||||
return users.getUserByUsername(username).then(user => {
|
||||
return credentials.getHardwareCredentialsOfUser(user.id).then(devices => {
|
||||
const options = simpleWebauthn.generateAssertionOptions({
|
||||
timeout: 60000,
|
||||
allowCredentials: devices.map(dev => ({
|
||||
id: dev.data.credentialID,
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
||||
})),
|
||||
userVerification: 'discouraged',
|
||||
rpID
|
||||
})
|
||||
|
||||
context.req.session.webauthn = {
|
||||
assertion: {
|
||||
challenge: options.challenge
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const validateAttestation = (userID, attestationResponse, context) => {
|
||||
const webauthnData = context.req.session.webauthn.attestation
|
||||
const expectedChallenge = webauthnData.challenge
|
||||
|
||||
return users.getUserById(userID).then(user => {
|
||||
return simpleWebauthn.verifyAttestationResponse({
|
||||
credential: attestationResponse,
|
||||
expectedChallenge: `${expectedChallenge}`,
|
||||
expectedOrigin,
|
||||
expectedRPID: rpID
|
||||
}).then(async verification => {
|
||||
const { verified, attestationInfo } = verification
|
||||
|
||||
if (verified && attestationInfo) {
|
||||
const {
|
||||
counter,
|
||||
credentialPublicKey,
|
||||
credentialID
|
||||
} = attestationInfo
|
||||
|
||||
const userDevices = await credentials.getHardwareCredentialsOfUser(user.id)
|
||||
const existingDevice = userDevices.find(device => device.data.credentialID === credentialID)
|
||||
|
||||
if (!existingDevice) {
|
||||
const newDevice = {
|
||||
counter,
|
||||
credentialPublicKey,
|
||||
credentialID
|
||||
}
|
||||
credentials.createHardwareCredential(user.id, newDevice)
|
||||
}
|
||||
}
|
||||
|
||||
context.req.session.webauthn = null
|
||||
return verified
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const validateAssertion = (username, rememberMe, assertionResponse, context) => {
|
||||
return users.getUserByUsername(username).then(user => {
|
||||
const expectedChallenge = context.req.session.webauthn.assertion.challenge
|
||||
|
||||
return credentials.getHardwareCredentialsOfUser(user.id).then(async devices => {
|
||||
const dbAuthenticator = _.find(dev => {
|
||||
return Buffer.from(dev.data.credentialID).compare(base64url.toBuffer(assertionResponse.rawId)) === 0
|
||||
}, devices)
|
||||
|
||||
if (!dbAuthenticator.data) {
|
||||
throw new Error(`Could not find authenticator matching ${assertionResponse.id}`)
|
||||
}
|
||||
|
||||
const convertedAuthenticator = _.merge(
|
||||
dbAuthenticator.data,
|
||||
{ credentialPublicKey: Buffer.from(dbAuthenticator.data.credentialPublicKey) }
|
||||
)
|
||||
|
||||
let verification
|
||||
try {
|
||||
verification = simpleWebauthn.verifyAssertionResponse({
|
||||
credential: assertionResponse,
|
||||
expectedChallenge: `${expectedChallenge}`,
|
||||
expectedOrigin,
|
||||
expectedRPID: rpID,
|
||||
authenticator: convertedAuthenticator
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
const { verified, assertionInfo } = verification
|
||||
|
||||
if (verified) {
|
||||
dbAuthenticator.data.counter = assertionInfo.newCounter
|
||||
await credentials.updateHardwareCredential(dbAuthenticator)
|
||||
|
||||
const finalUser = { id: user.id, username: user.username, role: user.role }
|
||||
context.req.session.user = finalUser
|
||||
if (rememberMe) context.req.session.cookie.maxAge = REMEMBER_ME_AGE
|
||||
}
|
||||
|
||||
context.req.session.webauthn = null
|
||||
return verified
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateAttestationOptions,
|
||||
generateAssertionOptions,
|
||||
validateAttestation,
|
||||
validateAssertion
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
const simpleWebauthn = require('@simplewebauthn/server')
|
||||
const base64url = require('base64url')
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const credentials = require('../../../../hardware-credentials')
|
||||
const T = require('../../../../time')
|
||||
const users = require('../../../../users')
|
||||
|
||||
const REMEMBER_ME_AGE = 90 * T.day
|
||||
|
||||
const rpID = `localhost`
|
||||
const expectedOrigin = `https://${rpID}:3001`
|
||||
|
||||
const generateAttestationOptions = (userID, session) => {
|
||||
return credentials.getHardwareCredentials().then(devices => {
|
||||
const options = simpleWebauthn.generateAttestationOptions({
|
||||
rpName: 'Lamassu',
|
||||
rpID,
|
||||
userName: `Usernameless user created at ${new Date().toISOString()}`,
|
||||
userID: userID,
|
||||
timeout: 60000,
|
||||
attestationType: 'direct',
|
||||
excludeCredentials: devices.map(dev => ({
|
||||
id: dev.data.credentialID,
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
||||
})),
|
||||
authenticatorSelection: {
|
||||
authenticatorAttachment: 'cross-platform',
|
||||
userVerification: 'discouraged',
|
||||
requireResidentKey: false
|
||||
}
|
||||
})
|
||||
|
||||
session.webauthn = {
|
||||
attestation: {
|
||||
challenge: options.challenge
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
}
|
||||
|
||||
const generateAssertionOptions = context => {
|
||||
return credentials.getHardwareCredentials().then(devices => {
|
||||
const options = simpleWebauthn.generateAssertionOptions({
|
||||
timeout: 60000,
|
||||
allowCredentials: devices.map(dev => ({
|
||||
id: dev.data.credentialID,
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'ble', 'nfc', 'internal']
|
||||
})),
|
||||
userVerification: 'discouraged',
|
||||
rpID
|
||||
})
|
||||
|
||||
context.req.session.webauthn = {
|
||||
assertion: {
|
||||
challenge: options.challenge
|
||||
}
|
||||
}
|
||||
return options
|
||||
})
|
||||
}
|
||||
|
||||
const validateAttestation = (userID, attestationResponse, context) => {
|
||||
const webauthnData = context.req.session.webauthn.attestation
|
||||
const expectedChallenge = webauthnData.challenge
|
||||
|
||||
return users.getUserById(userID).then(user => {
|
||||
return simpleWebauthn.verifyAttestationResponse({
|
||||
credential: attestationResponse,
|
||||
expectedChallenge: `${expectedChallenge}`,
|
||||
expectedOrigin,
|
||||
expectedRPID: rpID
|
||||
}).then(async verification => {
|
||||
const { verified, attestationInfo } = verification
|
||||
|
||||
if (verified && attestationInfo) {
|
||||
const {
|
||||
fmt,
|
||||
counter,
|
||||
aaguid,
|
||||
credentialPublicKey,
|
||||
credentialID,
|
||||
credentialType,
|
||||
userVerified,
|
||||
attestationObject
|
||||
} = attestationInfo
|
||||
|
||||
const userDevices = await credentials.getHardwareCredentialsOfUser(user.id)
|
||||
const existingDevice = userDevices.find(device => device.data.credentialID === credentialID)
|
||||
|
||||
if (!existingDevice) {
|
||||
const newDevice = {
|
||||
fmt,
|
||||
counter,
|
||||
aaguid,
|
||||
credentialPublicKey,
|
||||
credentialID,
|
||||
credentialType,
|
||||
userVerified,
|
||||
attestationObject
|
||||
}
|
||||
credentials.createHardwareCredential(user.id, newDevice)
|
||||
}
|
||||
}
|
||||
|
||||
context.req.session.webauthn = null
|
||||
return verified
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const validateAssertion = (assertionResponse, context) => {
|
||||
const expectedChallenge = context.req.session.webauthn.assertion.challenge
|
||||
|
||||
return credentials.getHardwareCredentials().then(async devices => {
|
||||
const dbAuthenticator = _.find(dev => {
|
||||
return Buffer.from(dev.data.credentialID).compare(base64url.toBuffer(assertionResponse.rawId)) === 0
|
||||
}, devices)
|
||||
|
||||
if (!dbAuthenticator.data) {
|
||||
throw new Error(`Could not find authenticator matching ${assertionResponse.id}`)
|
||||
}
|
||||
|
||||
const convertedAuthenticator = _.merge(
|
||||
dbAuthenticator.data,
|
||||
{ credentialPublicKey: Buffer.from(dbAuthenticator.data.credentialPublicKey) }
|
||||
)
|
||||
|
||||
let verification
|
||||
try {
|
||||
verification = simpleWebauthn.verifyAssertionResponse({
|
||||
credential: assertionResponse,
|
||||
expectedChallenge: `${expectedChallenge}`,
|
||||
expectedOrigin,
|
||||
expectedRPID: rpID,
|
||||
authenticator: convertedAuthenticator
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
const { verified, assertionInfo } = verification
|
||||
|
||||
if (verified) {
|
||||
dbAuthenticator.data.counter = assertionInfo.newCounter
|
||||
await credentials.updateHardwareCredential(dbAuthenticator)
|
||||
|
||||
const user = await users.getUserById(dbAuthenticator.user_id)
|
||||
const finalUser = { id: user.id, username: user.username, role: user.role }
|
||||
context.req.session.user = finalUser
|
||||
context.req.session.cookie.maxAge = REMEMBER_ME_AGE
|
||||
}
|
||||
|
||||
context.req.session.webauthn = null
|
||||
return verified
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateAttestationOptions,
|
||||
generateAssertionOptions,
|
||||
validateAttestation,
|
||||
validateAssertion
|
||||
}
|
||||
17
lib/new-admin/graphql/modules/authentication/index.js
Normal file
17
lib/new-admin/graphql/modules/authentication/index.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
const FIDO2FA = require('./FIDO2FAStrategy')
|
||||
const FIDOPasswordless = require('./FIDOPasswordlessStrategy')
|
||||
const FIDOUsernameless = require('./FIDOUsernamelessStrategy')
|
||||
|
||||
const STRATEGIES = {
|
||||
FIDO2FA,
|
||||
FIDOPasswordless,
|
||||
FIDOUsernameless
|
||||
}
|
||||
|
||||
// FIDO2FA, FIDOPasswordless or FIDOUsernameless
|
||||
const CHOSEN_STRATEGY = 'FIDOPasswordless'
|
||||
|
||||
module.exports = {
|
||||
CHOSEN_STRATEGY,
|
||||
strategy: STRATEGIES[CHOSEN_STRATEGY]
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
const otplib = require('otplib')
|
||||
const argon2 = require('argon2')
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const constants = require('../../../constants')
|
||||
const authTokens = require('../../../auth-tokens')
|
||||
|
|
@ -8,6 +9,7 @@ const T = require('../../../time')
|
|||
const users = require('../../../users')
|
||||
const sessionManager = require('../../../session-manager')
|
||||
const authErrors = require('../errors/authentication')
|
||||
const credentials = require('../../../hardware-credentials')
|
||||
|
||||
const REMEMBER_ME_AGE = 90 * T.day
|
||||
|
||||
|
|
@ -140,10 +142,14 @@ const deleteSession = (sessionID, context) => {
|
|||
}
|
||||
|
||||
const login = (username, password) => {
|
||||
return authenticateUser(username, password).then(user => {
|
||||
const twoFASecret = user.twofa_code
|
||||
return twoFASecret ? 'INPUT2FA' : 'SETUP2FA'
|
||||
})
|
||||
return authenticateUser(username, password)
|
||||
.then(user => {
|
||||
return Promise.all([credentials.getHardwareCredentialsOfUser(user.id), user.twofa_code])
|
||||
})
|
||||
.then(([devices, twoFASecret]) => {
|
||||
if (!_.isEmpty(devices)) return 'FIDO'
|
||||
return twoFASecret ? 'INPUT2FA' : 'SETUP2FA'
|
||||
})
|
||||
}
|
||||
|
||||
const input2FA = (username, password, rememberMe, code, context) => {
|
||||
|
|
@ -234,6 +240,7 @@ const reset2FA = (token, userID, code, context) => {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
authenticateUser,
|
||||
getUserData,
|
||||
get2FASecret,
|
||||
confirm2FA,
|
||||
|
|
@ -1,34 +1,81 @@
|
|||
const authentication = require('../modules/authentication')
|
||||
const userManagement = require('../modules/userManagement')
|
||||
const users = require('../../../users')
|
||||
const sessionManager = require('../../../session-manager')
|
||||
|
||||
const getFIDOStrategyQueries = () => {
|
||||
switch (authentication.CHOSEN_STRATEGY) {
|
||||
case 'FIDO2FA':
|
||||
return {
|
||||
generateAttestationOptions: (...[, { userID }, context]) => authentication.strategy.generateAttestationOptions(userID, context.req.session),
|
||||
generateAssertionOptions: (...[, { username, password }, context]) => authentication.strategy.generateAssertionOptions(username, password, context)
|
||||
}
|
||||
case 'FIDOPasswordless':
|
||||
return {
|
||||
generateAttestationOptions: (...[, { userID }, context]) => authentication.strategy.generateAttestationOptions(userID, context.req.session),
|
||||
generateAssertionOptions: (...[, { username }, context]) => authentication.strategy.generateAssertionOptions(username, context)
|
||||
}
|
||||
case 'FIDOUsernameless':
|
||||
return {
|
||||
generateAttestationOptions: (...[, { userID }, context]) => authentication.strategy.generateAttestationOptions(userID, context.req.session),
|
||||
generateAssertionOptions: (...[, { }, context]) => authentication.strategy.generateAssertionOptions(context)
|
||||
}
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const getFIDOStrategyMutations = () => {
|
||||
switch (authentication.CHOSEN_STRATEGY) {
|
||||
case 'FIDO2FA':
|
||||
return {
|
||||
validateAttestation: (...[, { userID, attestationResponse }, context]) => authentication.strategy.validateAttestation(userID, attestationResponse, context),
|
||||
validateAssertion: (...[, { username, password, rememberMe, assertionResponse }, context]) => authentication.strategy.validateAssertion(username, password, rememberMe, assertionResponse, context)
|
||||
}
|
||||
case 'FIDOPasswordless':
|
||||
return {
|
||||
validateAttestation: (...[, { userID, attestationResponse }, context]) => authentication.strategy.validateAttestation(userID, attestationResponse, context),
|
||||
validateAssertion: (...[, { username, rememberMe, assertionResponse }, context]) => authentication.strategy.validateAssertion(username, rememberMe, assertionResponse, context)
|
||||
}
|
||||
case 'FIDOUsernameless':
|
||||
return {
|
||||
validateAttestation: (...[, { userID, attestationResponse }, context]) => authentication.strategy.validateAttestation(userID, attestationResponse, context),
|
||||
validateAssertion: (...[, { assertionResponse }, context]) => authentication.strategy.validateAssertion(assertionResponse, context)
|
||||
}
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const resolver = {
|
||||
Query: {
|
||||
users: () => users.getUsers(),
|
||||
sessions: () => sessionManager.getSessions(),
|
||||
userSessions: (...[, { username }]) => sessionManager.getSessionsByUsername(username),
|
||||
userData: (...[, {}, context]) => authentication.getUserData(context),
|
||||
get2FASecret: (...[, { username, password }]) => authentication.get2FASecret(username, password),
|
||||
confirm2FA: (...[, { code }, context]) => authentication.confirm2FA(code, context),
|
||||
validateRegisterLink: (...[, { token }]) => authentication.validateRegisterLink(token),
|
||||
validateResetPasswordLink: (...[, { token }]) => authentication.validateResetPasswordLink(token),
|
||||
validateReset2FALink: (...[, { token }]) => authentication.validateReset2FALink(token)
|
||||
userData: (...[, {}, context]) => userManagement.getUserData(context),
|
||||
get2FASecret: (...[, { username, password }]) => userManagement.get2FASecret(username, password),
|
||||
confirm2FA: (...[, { code }, context]) => userManagement.confirm2FA(code, context),
|
||||
validateRegisterLink: (...[, { token }]) => userManagement.validateRegisterLink(token),
|
||||
validateResetPasswordLink: (...[, { token }]) => userManagement.validateResetPasswordLink(token),
|
||||
validateReset2FALink: (...[, { token }]) => userManagement.validateReset2FALink(token),
|
||||
...getFIDOStrategyQueries()
|
||||
},
|
||||
Mutation: {
|
||||
enableUser: (...[, { confirmationCode, id }, context]) => authentication.enableUser(confirmationCode, id, context),
|
||||
disableUser: (...[, { confirmationCode, id }, context]) => authentication.disableUser(confirmationCode, id, context),
|
||||
deleteSession: (...[, { sid }, context]) => authentication.deleteSession(sid, context),
|
||||
enableUser: (...[, { confirmationCode, id }, context]) => userManagement.enableUser(confirmationCode, id, context),
|
||||
disableUser: (...[, { confirmationCode, id }, context]) => userManagement.disableUser(confirmationCode, id, context),
|
||||
deleteSession: (...[, { sid }, context]) => userManagement.deleteSession(sid, context),
|
||||
deleteUserSessions: (...[, { username }]) => sessionManager.deleteSessionsByUsername(username),
|
||||
changeUserRole: (...[, { confirmationCode, id, newRole }, context]) => authentication.changeUserRole(confirmationCode, id, newRole, context),
|
||||
login: (...[, { username, password }]) => authentication.login(username, password),
|
||||
input2FA: (...[, { username, password, rememberMe, code }, context]) => authentication.input2FA(username, password, rememberMe, code, context),
|
||||
setup2FA: (...[, { username, password, rememberMe, codeConfirmation }, context]) => authentication.setup2FA(username, password, rememberMe, codeConfirmation, context),
|
||||
createResetPasswordToken: (...[, { confirmationCode, userID }, context]) => authentication.createResetPasswordToken(confirmationCode, userID, context),
|
||||
createReset2FAToken: (...[, { confirmationCode, userID }, context]) => authentication.createReset2FAToken(confirmationCode, userID, context),
|
||||
createRegisterToken: (...[, { username, role }]) => authentication.createRegisterToken(username, role),
|
||||
register: (...[, { token, username, password, role }]) => authentication.register(token, username, password, role),
|
||||
resetPassword: (...[, { token, userID, newPassword }, context]) => authentication.resetPassword(token, userID, newPassword, context),
|
||||
reset2FA: (...[, { token, userID, code }, context]) => authentication.reset2FA(token, userID, code, context)
|
||||
changeUserRole: (...[, { confirmationCode, id, newRole }, context]) => userManagement.changeUserRole(confirmationCode, id, newRole, context),
|
||||
login: (...[, { username, password }]) => userManagement.login(username, password),
|
||||
input2FA: (...[, { username, password, rememberMe, code }, context]) => userManagement.input2FA(username, password, rememberMe, code, context),
|
||||
setup2FA: (...[, { username, password, rememberMe, codeConfirmation }, context]) => userManagement.setup2FA(username, password, rememberMe, codeConfirmation, context),
|
||||
createResetPasswordToken: (...[, { confirmationCode, userID }, context]) => userManagement.createResetPasswordToken(confirmationCode, userID, context),
|
||||
createReset2FAToken: (...[, { confirmationCode, userID }, context]) => userManagement.createReset2FAToken(confirmationCode, userID, context),
|
||||
createRegisterToken: (...[, { username, role }]) => userManagement.createRegisterToken(username, role),
|
||||
register: (...[, { token, username, password, role }]) => userManagement.register(token, username, password, role),
|
||||
resetPassword: (...[, { token, userID, newPassword }, context]) => userManagement.resetPassword(token, userID, newPassword, context),
|
||||
reset2FA: (...[, { token, userID, code }, context]) => userManagement.reset2FA(token, userID, code, context),
|
||||
...getFIDOStrategyMutations()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,37 @@
|
|||
const authentication = require('../modules/authentication')
|
||||
|
||||
const getFIDOStrategyQueryTypes = () => {
|
||||
switch (authentication.CHOSEN_STRATEGY) {
|
||||
case 'FIDO2FA':
|
||||
return `generateAttestationOptions(userID: ID!): JSONObject
|
||||
generateAssertionOptions(username: String!, password: String!): JSONObject`
|
||||
case 'FIDOPasswordless':
|
||||
return `generateAttestationOptions(userID: ID!): JSONObject
|
||||
generateAssertionOptions(username: String!): JSONObject`
|
||||
case 'FIDOUsernameless':
|
||||
return `generateAttestationOptions(userID: ID!): JSONObject
|
||||
generateAssertionOptions: JSONObject`
|
||||
default:
|
||||
return ``
|
||||
}
|
||||
}
|
||||
|
||||
const getFIDOStrategyMutationsTypes = () => {
|
||||
switch (authentication.CHOSEN_STRATEGY) {
|
||||
case 'FIDO2FA':
|
||||
return `validateAttestation(userID: ID!, attestationResponse: JSONObject!): Boolean
|
||||
validateAssertion(username: String!, password: String!, rememberMe: Boolean!, assertionResponse: JSONObject!): Boolean`
|
||||
case 'FIDOPasswordless':
|
||||
return `validateAttestation(userID: ID!, attestationResponse: JSONObject!): Boolean
|
||||
validateAssertion(username: String!, rememberMe: Boolean!, assertionResponse: JSONObject!): Boolean`
|
||||
case 'FIDOUsernameless':
|
||||
return `validateAttestation(userID: ID!, attestationResponse: JSONObject!): Boolean
|
||||
validateAssertion(assertionResponse: JSONObject!): Boolean`
|
||||
default:
|
||||
return ``
|
||||
}
|
||||
}
|
||||
|
||||
const typeDef = `
|
||||
directive @auth(
|
||||
requires: [Role] = [USER, SUPERUSER]
|
||||
|
|
@ -54,6 +88,7 @@ const typeDef = `
|
|||
validateRegisterLink(token: String!): User
|
||||
validateResetPasswordLink(token: String!): User
|
||||
validateReset2FALink(token: String!): TwoFactorSecret
|
||||
${getFIDOStrategyQueryTypes()}
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
|
@ -72,6 +107,7 @@ const typeDef = `
|
|||
register(token: String!, username: String!, password: String!, role: String!): Boolean
|
||||
resetPassword(token: String!, userID: ID!, newPassword: String!): Boolean
|
||||
reset2FA(token: String!, userID: ID!, code: String!): Boolean
|
||||
${getFIDOStrategyMutationsTypes()}
|
||||
}
|
||||
`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue