Skip to content

Commit

Permalink
Update call to generate_registration_options
Browse files Browse the repository at this point in the history
This implements Option 2 of the webauthn migration notes
(https://github.com/duo-labs/py_webauthn/releases/tag/v2.0.0)
by adding a new field `webauthn_id` to the user model that is
automatically generated using a helper method by webauthn.
  • Loading branch information
david-venhoff committed Mar 28, 2024
1 parent b99a51b commit b952142
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 6 deletions.
24 changes: 24 additions & 0 deletions integreat_cms/cms/migrations/0088_user_webauthn_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.10 on 2024-03-28 18:24

from django.db import migrations, models
from webauthn.helpers import generate_user_handle


class Migration(migrations.Migration):
"""
Add a webauthn id bytes field to the user model
"""

dependencies = [
("cms", "0087_language_social_media_webapp_description_and_more"),
]

operations = [
migrations.AddField(
model_name="user",
name="webauthn_id",
field=models.BinaryField(
default=generate_user_handle,
),
),
]
2 changes: 2 additions & 0 deletions integreat_cms/cms/models/users/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from webauthn.helpers import generate_user_handle

if TYPE_CHECKING:
from datetime import datetime
Expand Down Expand Up @@ -139,6 +140,7 @@ class User(AbstractUser, AbstractBaseModel):
"Enable this option to activate the passwordless login routine for this account"
),
)
webauthn_id = models.BinaryField(default=generate_user_handle)

#: Custom model manager for user objects
objects = CustomUserManager()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
make_credential_options = generate_registration_options(
rp_name="Integreat",
rp_id=settings.HOSTNAME,
user_id=str(user.id),
user_id=user.webauthn_id,
user_name=user.username,
user_display_name=user.first_name + " " + user.last_name,
)
Expand Down
12 changes: 7 additions & 5 deletions integreat_cms/static/src/js/utils/mfa-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ type CredentialResponseFromServer = {
};
};

// Based on https://github.com/duo-labs/py_webauthn/blob/master/flask_demo/static/js/webauthn.js
// Based on https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/browser/src/helpers/bufferToBase64URLString.ts
export const b64enc = (buf: Uint8Array) =>
base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");

// Based on https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/browser/src/helpers/base64URLStringToBuffer.ts
const b64dec = (b64url: string) =>
Uint8Array.from(atob(b64url.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));

const b64RawEnc = (buf: Uint8Array) => base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");

export const transformCredentialRequestOptions = (
Expand Down Expand Up @@ -79,11 +83,9 @@ export const transformAssertionForServer = (newAssertion: PublicKeyCredential) =

export const transformCredentialCreateOptions = (credentialCreateOptionsFromServer: CredentialResponseFromServer) => {
const { challenge, user } = credentialCreateOptionsFromServer;
const userIdData = Uint8Array.from(credentialCreateOptionsFromServer.user.id, (c) => c.charCodeAt(0));

const challengeData = Uint8Array.from(atob(challenge.replace(/-/g, "+").replace(/_/g, "/")), (c) =>
c.charCodeAt(0)
);
const userIdData = b64dec(user.id);
const challengeData = b64dec(challenge);

const transformedCredentialCreateOptions = {
...credentialCreateOptionsFromServer,
Expand Down

0 comments on commit b952142

Please sign in to comment.