-
-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Webautn support #3238
Open
Spitfireap
wants to merge
6
commits into
feature/sievefilters
Choose a base branch
from
feature/fido
base: feature/sievefilters
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
WIP: Webautn support #3238
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
15d391c
Added Fido API
Spitfireap eeb4b26
Updated dev reqs
Spitfireap a707543
Enabled fido registration on frontend
Spitfireap 2b36c06
Fixed settings
Spitfireap e520b2d
Added Webautn login
Spitfireap c810684
Few updates
tonioo File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,3 +34,5 @@ test_project/modoboa.db | |
.ash_history | ||
modoboa.kdev4 | ||
.kateproject | ||
test_project/cert.crt | ||
test_project/cert.key |
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 |
---|---|---|
|
@@ -3,3 +3,6 @@ django-debug-toolbar | |
pre-commit | ||
black | ||
pylint | ||
django-extensions | ||
Werkzeug | ||
pyOpenSSL |
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
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
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 |
---|---|---|
@@ -1,7 +1,7 @@ | ||
{ | ||
"API_BASE_URL": "/api/v2/", | ||
"API_DOC_URL": "/api/schema-v2/swagger/", | ||
"OAUTH_AUTHORITY_URL": "http://localhost:8000/api/o", | ||
"OAUTH_AUTHORITY_URL": "https://localhost:8000/api/o", | ||
"OAUTH_CLIENT_ID": "LVQbfIIX3khWR3nDvix1u9yEGHZUxcx53bhJ7FlD", | ||
"OAUTH_REDIRECT_URI": "http://localhost:3000/login/logged" | ||
"OAUTH_REDIRECT_URI": "https://localhost:3000/login/logged" | ||
} |
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,225 @@ | ||
<template> | ||
<div> | ||
<v-card flat> | ||
<v-card-title> | ||
<span class="text-subtitle-1"> | ||
{{ $gettext('WebAuthn') }} | ||
</span> | ||
</v-card-title> | ||
<v-card-text> | ||
<div tag="p" class="my-4"> | ||
{{ | ||
$gettext( | ||
"You don't have any WebAuthN registered as a second authentication method." | ||
) | ||
}} | ||
</div> | ||
<template v-if="browserCapable"> | ||
<v-form ref="fidoForm" @submit.prevent="startFidoRegistration"> | ||
<v-text-field | ||
v-model="name" | ||
:label="$gettext('Name')" | ||
:rules="[rules.required]" | ||
/> | ||
<v-btn color="success" type="submit" :loading="registrationLoading"> | ||
{{ $gettext('Add WebAuthN device') }} | ||
</v-btn> | ||
</v-form> | ||
</template> | ||
<template v-else> | ||
<v-alert type="error"> | ||
{{ $gettext('Your browser does not seem compatible with WebAuthN') }} | ||
</v-alert> | ||
</template> | ||
</v-card-text> | ||
</v-card> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="js"> | ||
import { ref, onMounted } from 'vue' | ||
import authApi from '@/api/auth.js' | ||
import { useGettext } from 'vue3-gettext' | ||
import rules from '@/plugins/rules.js' | ||
|
||
const { $gettext } = useGettext() | ||
|
||
const name = ref() | ||
const registrationLoading = ref(false) | ||
const creationOption = ref() | ||
const browserCapable = !!(navigator.credentials && navigator.credentials.create && navigator.credentials.get && window.PublicKeyCredential) | ||
const fidoForm = ref() | ||
|
||
async function startFidoRegistration() { | ||
const { valid } = await fidoForm.value.validate() | ||
if (!valid) { | ||
return | ||
} | ||
if (creationOption.value) { | ||
navigator.credentials.create({...creationOption.value}) | ||
.then(function (attestation) { | ||
const result = createResponseToJSON(attestation) | ||
result.name = name.value | ||
console.log(result) | ||
authApi.endFidoRegistration(result).then((resp) => { | ||
console.log(resp) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
onMounted(() => { | ||
registrationLoading.value = true | ||
authApi.getAllFidoRegistred().then((resp) => { | ||
console.log(resp) | ||
}) | ||
authApi.beginFidoRegistration().then((resp) => { | ||
creationOption.value = createRequestFromJSON(resp.data) | ||
}).finally(() => registrationLoading.value = false) | ||
}) | ||
|
||
// Taken from the example of python-fido2 repo | ||
// src/webauthn-json/base64url.ts | ||
function base64urlToBuffer(baseurl64String) { | ||
const padding = "==".slice(0, (4 - baseurl64String.length % 4) % 4); | ||
const base64String = baseurl64String.replace(/-/g, "+").replace(/_/g, "/") + padding; | ||
const str = atob(base64String); | ||
const buffer = new ArrayBuffer(str.length); | ||
const byteView = new Uint8Array(buffer); | ||
for (let i = 0; i < str.length; i++) { | ||
byteView[i] = str.charCodeAt(i); | ||
} | ||
return buffer; | ||
} | ||
|
||
function bufferToBase64url(buffer) { | ||
const byteView = new Uint8Array(buffer); | ||
let str = ""; | ||
for (const charCode of byteView) { | ||
str += String.fromCharCode(charCode); | ||
} | ||
const base64String = btoa(str); | ||
const base64urlString = base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); | ||
return base64urlString; | ||
} | ||
|
||
// src/webauthn-json/convert.ts | ||
const copyValue = "copy"; | ||
const convertValue = "convert"; | ||
function convert(conversionFn, schema, input) { | ||
if (schema === copyValue) { | ||
return input; | ||
} | ||
if (schema === convertValue) { | ||
return conversionFn(input); | ||
} | ||
if (schema instanceof Array) { | ||
return input.map((v) => convert(conversionFn, schema[0], v)); | ||
} | ||
if (schema instanceof Object) { | ||
const output = {}; | ||
for (const [key, schemaField] of Object.entries(schema)) { | ||
if (schemaField.derive) { | ||
const v = schemaField.derive(input); | ||
if (v !== void 0) { | ||
input[key] = v; | ||
} | ||
} | ||
if (!(key in input)) { | ||
if (schemaField.required) { | ||
throw new Error(`Missing key: ${key}`); | ||
} | ||
continue; | ||
} | ||
if (input[key] == null) { | ||
output[key] = null; | ||
continue; | ||
} | ||
output[key] = convert(conversionFn, schemaField.schema, input[key]); | ||
} | ||
return output; | ||
} | ||
} | ||
|
||
function derived(schema, derive) { | ||
return { | ||
required: true, | ||
schema, | ||
derive | ||
}; | ||
} | ||
function required(schema) { | ||
return { | ||
required: true, | ||
schema | ||
}; | ||
} | ||
function optional(schema) { | ||
return { | ||
required: false, | ||
schema | ||
}; | ||
} | ||
|
||
const publicKeyCredentialDescriptorSchema = { | ||
type: required(copyValue), | ||
id: required(convertValue), | ||
transports: optional(copyValue) | ||
} | ||
|
||
const simplifiedExtensionsSchema = { | ||
appid: optional(copyValue), | ||
appidExclude: optional(copyValue), | ||
credProps: optional(copyValue) | ||
} | ||
|
||
const simplifiedClientExtensionResultsSchema = { | ||
appid: optional(copyValue), | ||
appidExclude: optional(copyValue), | ||
credProps: optional(copyValue) | ||
} | ||
|
||
const credentialCreationOptions = { | ||
publicKey: required({ | ||
rp: required(copyValue), | ||
user: required({ | ||
id: required(convertValue), | ||
name: required(copyValue), | ||
displayName: required(copyValue) | ||
}), | ||
challenge: required(convertValue), | ||
pubKeyCredParams: required(copyValue), | ||
timeout: optional(copyValue), | ||
excludeCredentials: optional([publicKeyCredentialDescriptorSchema]), | ||
authenticatorSelection: optional(copyValue), | ||
attestation: optional(copyValue), | ||
extensions: optional(simplifiedExtensionsSchema) | ||
}), | ||
signal: optional(copyValue) | ||
} | ||
|
||
const publicKeyCredentialWithAttestation = { | ||
type: required(copyValue), | ||
id: required(copyValue), | ||
rawId: required(convertValue), | ||
authenticatorAttachment: optional(copyValue), | ||
response: required({ | ||
clientDataJSON: required(convertValue), | ||
attestationObject: required(convertValue), | ||
transports: derived(copyValue, (response) => { | ||
var _a; | ||
return ((_a = response.getTransports) == null ? void 0 : _a.call(response)) || []; | ||
}) | ||
}), | ||
clientExtensionResults: derived(simplifiedClientExtensionResultsSchema, (pkc) => pkc.getClientExtensionResults()) | ||
} | ||
|
||
function createRequestFromJSON(requestJSON) { | ||
return convert(base64urlToBuffer, credentialCreationOptions, requestJSON); | ||
} | ||
|
||
function createResponseToJSON(credential) { | ||
return convert(bufferToBase64url, publicKeyCredentialWithAttestation, credential); | ||
} | ||
|
||
</script> |
54 changes: 54 additions & 0 deletions
54
frontend/src/components/account/FidoRegistrationDialog.vue
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,54 @@ | ||
<template> | ||
<v-card> | ||
<v-card-title> | ||
<span class="headline"> {{ $gettext('WebAuthN registration') }} </span> | ||
</v-card-title> | ||
<v-card-text> | ||
|
||
<v-btn color="success" :loading="registrationLoading" @click="startFidoRegistration">{{ $gettext('Register device') }}</v-btn> | ||
</v-card-text> | ||
<v-card-actions> | ||
<v-spacer></v-spacer> | ||
<v-btn color="grey darken-1" text @click="close"> | ||
{{ $gettext('Close') }} | ||
</v-btn> | ||
</v-card-actions> | ||
</v-card> | ||
</template> | ||
|
||
<script setup lang="js"> | ||
import { useGettext } from 'vue3-gettext' | ||
import { ref, onMounted } from 'vue' | ||
|
||
|
||
const { $gettext } = useGettext() | ||
const name = ref() | ||
const registrationLoading = ref(false) | ||
const publicKey = ref() | ||
|
||
async function startFidoRegistration() { | ||
if (publicKey.value) { | ||
navigator.credentials.create(publicKey.value) | ||
.then(function (attestation) { | ||
// Send new credential info to server for verification and registration. | ||
console.log(attestation) | ||
}) | ||
.catch(function (err) { | ||
// No acceptable authenticator or user refused consent. Handle appropriately. | ||
console.log(err) | ||
}) | ||
} | ||
} | ||
|
||
onMounted(() => { | ||
registrationLoading.value = true | ||
authApi.beginFidoRegistration().then((resp) => { | ||
publicKey.value = resp.data.publicKey | ||
}).finally(() => registrationLoading.value = false) | ||
}) | ||
|
||
const emit = defineEmits(['close']) | ||
function close() { | ||
emit('close') | ||
} | ||
</script> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file you mention here is listed in gitignore file, so I guess it won't work as expected. We should find a way to add this somewhere in the doc