From 500feba50d0071424b5a40d6ec2ca8a5552671fc Mon Sep 17 00:00:00 2001 From: Leo Hemsted Date: Mon, 10 May 2021 16:36:00 +0100 Subject: [PATCH] add name/id and consolidate webauthn types in model/table so we can be in line with what the admin handles, and keep it simple on the api side and do as little manipulation of binary data as possible. ### Minor changes * id is a UUID we can use for referencing within notify. No relation to the key itself. * name is a user viewable name that can be set/edited * fix updated_at to have onupdate, not default ### Simplify the webauthn data credential_data is the data we store about an authenticator that we'll use to identify the key when logging in. includes the credential_id, the public_key, and the aaguid (which identifies the authenticator make/model) registration_response is the data containing audit information - in the future we can use this to ensure that the authenticators used are of high quality. both of these fields are CBOR (a kind of binary json), encoded in base64 so that they can be embedded within our regular JSON api endpoints. we don't anticipate the api ever needing to interact with this data directly. --- app/models.py | 17 ++++++++++------ .../versions/0355_add_webauthn_table.py | 20 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/models.py b/app/models.py index 7abeaf848..9c471b5f2 100644 --- a/app/models.py +++ b/app/models.py @@ -2600,13 +2600,18 @@ class WebauthnCredential(db.Model): """ __tablename__ = "webauthn_credential" - credential_id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - aaguid = db.Column(UUID(as_uuid=True), default=uuid.uuid4, nullable=False) - public_key = db.Column(db.String, nullable=False) + id = db.Column(UUID(as_uuid=True), primary_key=True, nullable=False, default=uuid.uuid4) - user_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), primary_key=True, nullable=False) + user_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), nullable=False) + user = db.relationship(User, backref=db.backref("webauthn_credentials")) - registration_response = db.Column(JSONB(none_as_null=True), nullable=False, default={}) + name = db.Column(db.String, nullable=False) + + # base64 encoded CBOR. used for logging in. https://w3c.github.io/webauthn/#sctn-attested-credential-data + credential_data = db.Column(db.String, nullable=False) + + # base64 encoded CBOR. used for auditing. https://www.w3.org/TR/webauthn-2/#authenticatorattestationresponse + registration_response = db.Column(db.String, nullable=False) created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) - updated_at = db.Column(db.DateTime, nullable=True, default=datetime.datetime.utcnow) + updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow) diff --git a/migrations/versions/0355_add_webauthn_table.py b/migrations/versions/0355_add_webauthn_table.py index 6e25ede16..ed0e23967 100644 --- a/migrations/versions/0355_add_webauthn_table.py +++ b/migrations/versions/0355_add_webauthn_table.py @@ -14,24 +14,22 @@ down_revision = '0354_government_channel' def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### op.create_table( 'webauthn_credential', - sa.Column('credential_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('aaguid', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('public_key', sa.String(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('registration_response', postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), nullable=False), + sa.Column('name', sa.String(), nullable=False), + + sa.Column('credential_data', sa.String(), nullable=False), + sa.Column('registration_response', sa.String(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=False), sa.Column('updated_at', sa.DateTime(), nullable=True), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), - sa.PrimaryKeyConstraint('credential_id', 'user_id') - ) - # ### end Alembic commands ### + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### op.drop_table('webauthn_credential') - # ### end Alembic commands ###