feat: provide webauthn device description from frontend on registration (#4363)
parent
bbc9e6422e
commit
92b3a5804b
|
@ -1021,6 +1021,9 @@ components:
|
|||
type: string
|
||||
format: byte
|
||||
webauthn.CredentialAttestationResponse:
|
||||
type: object
|
||||
properties:
|
||||
credential:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/webauthn.PublicKeyCredential'
|
||||
- type: object
|
||||
|
@ -1038,6 +1041,8 @@ components:
|
|||
attestationObject:
|
||||
type: string
|
||||
format: byte
|
||||
description:
|
||||
type: string
|
||||
webauthn.CredentialAssertionResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/webauthn.PublicKeyCredential'
|
||||
|
|
|
@ -2,6 +2,7 @@ package handlers
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
|
@ -83,6 +84,11 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
|
|||
|
||||
// WebauthnAttestationPOST processes the attestation challenge response from the client.
|
||||
func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
|
||||
type requestPostData struct {
|
||||
Credential json.RawMessage `json:"credential"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
w *webauthn.WebAuthn
|
||||
|
@ -90,6 +96,7 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
|
|||
|
||||
attestationResponse *protocol.ParsedCredentialCreationData
|
||||
credential *webauthn.Credential
|
||||
postData *requestPostData
|
||||
)
|
||||
|
||||
userSession := ctx.GetSession()
|
||||
|
@ -110,7 +117,16 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
|
|||
return
|
||||
}
|
||||
|
||||
if attestationResponse, err = protocol.ParseCredentialCreationResponseBody(bytes.NewReader(ctx.PostBody())); err != nil {
|
||||
err = json.Unmarshal(ctx.PostBody(), &postData)
|
||||
if err != nil {
|
||||
ctx.Logger.Errorf("Unable to parse %s assertion request data for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
|
||||
|
||||
respondUnauthorized(ctx, messageMFAValidationFailed)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if attestationResponse, err = protocol.ParseCredentialCreationResponseBody(bytes.NewReader(postData.Credential)); err != nil {
|
||||
ctx.Logger.Errorf("Unable to parse %s assertion for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
|
||||
|
||||
respondUnauthorized(ctx, messageMFAValidationFailed)
|
||||
|
@ -134,7 +150,7 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
|
|||
return
|
||||
}
|
||||
|
||||
device := model.NewWebauthnDeviceFromCredential(w.Config.RPID, userSession.Username, "Primary", credential)
|
||||
device := model.NewWebauthnDeviceFromCredential(w.Config.RPID, userSession.Username, postData.Description, credential)
|
||||
|
||||
if err = ctx.Providers.StorageProvider.SaveWebauthnDevice(ctx, device); err != nil {
|
||||
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
|
||||
|
|
|
@ -314,10 +314,14 @@ export async function getAssertionPublicKeyCredentialResult(
|
|||
|
||||
async function postAttestationPublicKeyCredentialResult(
|
||||
credential: AttestationPublicKeyCredential,
|
||||
description: string,
|
||||
): Promise<AxiosResponse<OptionalDataServiceResponse<any>>> {
|
||||
const credentialJSON = encodeAttestationPublicKeyCredential(credential);
|
||||
|
||||
return axios.post<OptionalDataServiceResponse<any>>(WebauthnAttestationPath, credentialJSON);
|
||||
const postBody = {
|
||||
credential: credentialJSON,
|
||||
description: description,
|
||||
};
|
||||
return axios.post<OptionalDataServiceResponse<any>>(WebauthnAttestationPath, postBody);
|
||||
}
|
||||
|
||||
export async function postAssertionPublicKeyCredentialResult(
|
||||
|
@ -327,11 +331,10 @@ export async function postAssertionPublicKeyCredentialResult(
|
|||
workflowID?: string,
|
||||
): Promise<AxiosResponse<ServiceResponse<SignInResponse>>> {
|
||||
const credentialJSON = encodeAssertionPublicKeyCredential(credential, targetURL, workflow, workflowID);
|
||||
|
||||
return axios.post<ServiceResponse<SignInResponse>>(WebauthnAssertionPath, credentialJSON);
|
||||
}
|
||||
|
||||
export async function performAttestationCeremony(token: string): Promise<AttestationResult> {
|
||||
export async function performAttestationCeremony(token: string, description: string): Promise<AttestationResult> {
|
||||
const attestationCreationOpts = await getAttestationCreationOptions(token);
|
||||
|
||||
if (attestationCreationOpts.status !== 200 || attestationCreationOpts.options == null) {
|
||||
|
@ -350,7 +353,7 @@ export async function performAttestationCeremony(token: string): Promise<Attesta
|
|||
return AttestationResult.Failure;
|
||||
}
|
||||
|
||||
const response = await postAttestationPublicKeyCredentialResult(attestationResult.credential);
|
||||
const response = await postAttestationPublicKeyCredentialResult(attestationResult.credential, description);
|
||||
|
||||
if (response.data.status === "OK" && (response.status === 200 || response.status === 201)) {
|
||||
return AttestationResult.Success;
|
||||
|
|
|
@ -12,6 +12,8 @@ import { FirstFactorPath } from "@services/Api";
|
|||
import { performAttestationCeremony } from "@services/Webauthn";
|
||||
import { extractIdentityToken } from "@utils/IdentityToken";
|
||||
|
||||
const description = "Primary";
|
||||
|
||||
const RegisterWebauthn = function () {
|
||||
const styles = useStyles();
|
||||
const navigate = useNavigate();
|
||||
|
@ -32,7 +34,7 @@ const RegisterWebauthn = function () {
|
|||
try {
|
||||
setRegistrationInProgress(true);
|
||||
|
||||
const result = await performAttestationCeremony(processToken);
|
||||
const result = await performAttestationCeremony(processToken, description);
|
||||
|
||||
setRegistrationInProgress(false);
|
||||
|
||||
|
|
Loading…
Reference in New Issue