Skip to content
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

Tn/fix authn bug #800

Merged
merged 4 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/authschemes/auth_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func (ah AShirtAuthBridge) DeleteSession(w http.ResponseWriter, r *http.Request)
// UserAuthData is a small structure capturing data relevant to a user for authentication purposes
type UserAuthData struct {
UserID int64 `db:"user_id"`
AuthnID []byte `db:"authn_id"`
Username string `db:"username"`
EncryptedPassword []byte `db:"encrypted_password"`
NeedsPasswordReset bool `db:"must_reset_password"`
Expand Down Expand Up @@ -363,7 +364,7 @@ func (ah AShirtAuthBridge) AddScheduledEmail(emailAddress string, userID int64,
// normal column names (i.e. no aliasing is happening)
func (ah AShirtAuthBridge) buildFindUserAuthQuery() sq.SelectBuilder {
return sq.Select(
"user_id", "username", "encrypted_password",
"user_id", "authn_id", "username", "encrypted_password",
"must_reset_password", "totp_secret", "json_data").
From("auth_scheme_data")
}
Expand Down
1 change: 1 addition & 0 deletions backend/authschemes/global_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func CreateNewAuthForUserGeneric(db *database.Connection, authSchemeName, authSc
"auth_type": authSchemeType,
"username": data.Username,
"user_id": data.UserID,
"authn_id": string(data.AuthnID),
"encrypted_password": data.EncryptedPassword,
"totp_secret": data.TOTPSecret,
"must_reset_password": data.NeedsPasswordReset,
Expand Down
2 changes: 1 addition & 1 deletion backend/authschemes/webauthn/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package webauthn
import (
"encoding/gob"

auth "github.com/duo-labs/webauthn/webauthn"
auth "github.com/go-webauthn/webauthn/webauthn"
)

type webAuthNSessionData struct {
Expand Down
2 changes: 1 addition & 1 deletion backend/authschemes/webauthn/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package webauthn
import (
"time"

auth "github.com/duo-labs/webauthn/webauthn"
auth "github.com/go-webauthn/webauthn/webauthn"
)

type RegistrationType int
Expand Down
10 changes: 7 additions & 3 deletions backend/authschemes/webauthn/webauthn.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
"github.com/theparanoids/ashirt-server/backend/server/middleware"
"github.com/theparanoids/ashirt-server/backend/server/remux"

"github.com/duo-labs/webauthn/protocol"
auth "github.com/duo-labs/webauthn/webauthn"
"github.com/go-webauthn/webauthn/protocol"
auth "github.com/go-webauthn/webauthn/webauthn"
)

type WebAuthn struct {
Expand Down Expand Up @@ -139,8 +139,12 @@ func (a WebAuthn) BindRoutes(r *mux.Router, bridge authschemes.AShirtAuthBridge)
return nil, backend.WrapError("Unable to create user", err)
}

rawSessionData := bridge.ReadAuthSchemeSession(r)
sessionData, _ := rawSessionData.(*webAuthNSessionData)

return nil, bridge.CreateNewAuthForUser(authschemes.UserAuthData{
UserID: userResult.UserID,
AuthnID: sessionData.UserData.AuthnID,
Username: data.UserData.UserName,
JSONData: helpers.Ptr(string(encodedCreds)),
})
Expand Down Expand Up @@ -382,7 +386,7 @@ func (a WebAuthn) beginLogin(w http.ResponseWriter, r *http.Request, bridge auth
return nil, backend.WebauthnLoginError(err, "Unable to parse webauthn credentials")
}

webauthnUser := makeWebAuthnUser(user.FirstName, user.LastName, username, user.Email, user.ID, creds)
webauthnUser := makeWebAuthnUser(user.FirstName, user.LastName, username, user.Email, user.ID, authData.AuthnID, creds)
options, sessionData, err := a.Web.BeginLogin(&webauthnUser)
if err != nil {
return nil, backend.WebauthnLoginError(err, "Unable to begin login process")
Expand Down
12 changes: 7 additions & 5 deletions backend/authschemes/webauthn/webauthnuser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"strings"
"time"

auth "github.com/duo-labs/webauthn/webauthn"
auth "github.com/go-webauthn/webauthn/webauthn"
"github.com/google/uuid"
"github.com/theparanoids/ashirt-server/backend/helpers"
)

type webauthnUser struct {
UserID []byte
AuthnID []byte
UserName string
IconURL string
Credentials []AShirtWebauthnCredential
Expand All @@ -24,7 +25,7 @@ type webauthnUser struct {

func makeNewWebAuthnUser(firstName, lastName, email, username, keyName string) webauthnUser {
return webauthnUser{
UserID: []byte(uuid.New().String()),
AuthnID: []byte(uuid.New().String()),
UserName: username,
FirstName: firstName,
LastName: lastName,
Expand All @@ -49,9 +50,10 @@ func makeAddKeyWebAuthnUser(userID int64, username, keyName string, creds []AShi
return user
}

func makeWebAuthnUser(firstName, lastName, username, email string, userID int64, creds []AShirtWebauthnCredential) webauthnUser {
func makeWebAuthnUser(firstName, lastName, username, email string, UserID int64, authnID []byte, creds []AShirtWebauthnCredential) webauthnUser {
return webauthnUser{
UserID: i64ToByteSlice(userID),
AuthnID: authnID,
UserID: i64ToByteSlice(UserID),
UserName: username,
Credentials: creds,
FirstName: firstName,
Expand All @@ -73,7 +75,7 @@ func byteSliceToI64(b []byte) int64 {
}

func (u *webauthnUser) WebAuthnID() []byte {
return u.UserID
return u.AuthnID
}

func (u *webauthnUser) WebAuthnName() string {
Expand Down
2 changes: 1 addition & 1 deletion backend/config/authconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"os"
"strings"

"github.com/duo-labs/webauthn/protocol"
"github.com/go-webauthn/webauthn/protocol"
"github.com/kelseyhightower/envconfig"
"github.com/theparanoids/ashirt-server/backend/helpers"
)
Expand Down
9 changes: 9 additions & 0 deletions backend/migrations/20230324124303-add-authn-id.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- +migrate Up
ALTER TABLE auth_scheme_data
ADD COLUMN authn_id VARCHAR(255) AFTER user_id
;

-- +migrate Down
ALTER TABLE auth_scheme_data
DROP COLUMN authn_id
;
22 changes: 12 additions & 10 deletions backend/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package models

import (
"database/sql"
"time"

"github.com/theparanoids/ashirt-server/backend/policy"
Expand Down Expand Up @@ -175,16 +176,17 @@ type AuthSchemeData struct {
// AuthScheme defines the name of the authentication method. e.g. Okta
AuthScheme string `db:"auth_scheme"`
// AuthType defines how the scheme should work. e.g. "oidc" or "local"
AuthType string `db:"auth_type"`
Username string `db:"username"`
UserID int64 `db:"user_id"`
EncryptedPassword []byte `db:"encrypted_password"`
MustResetPassword bool `db:"must_reset_password"`
TOTPSecret *string `db:"totp_secret"`
JSONData *string `db:"json_data"`
LastLogin *time.Time `db:"last_login"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
AuthType string `db:"auth_type"`
Username string `db:"username"`
AuthnID sql.NullString `db:"authn_id"`
UserID int64 `db:"user_id"`
EncryptedPassword []byte `db:"encrypted_password"`
MustResetPassword bool `db:"must_reset_password"`
TOTPSecret *string `db:"totp_secret"`
JSONData *string `db:"json_data"`
LastLogin *time.Time `db:"last_login"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
}

// Session reflects the structure of the database table 'sessions'
Expand Down
7 changes: 4 additions & 3 deletions backend/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ CREATE TABLE `auth_scheme_data` (
`auth_type` varchar(255) NOT NULL,
`username` varchar(255) NOT NULL,
`user_id` int DEFAULT NULL,
`authn_id` varchar(255) DEFAULT NULL,
`encrypted_password` varbinary(255) DEFAULT NULL,
`must_reset_password` tinyint(1) DEFAULT '0',
`totp_secret` varchar(255) DEFAULT NULL,
Expand Down Expand Up @@ -489,7 +490,7 @@ CREATE TABLE `users` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2022-12-22 20:05:00
-- Dump completed on 2023-03-24 12:59:30
-- MySQL dump 10.13 Distrib 8.0.31, for Linux (aarch64)
--
-- Host: localhost Database: migrate_db
Expand All @@ -513,7 +514,7 @@ CREATE TABLE `users` (

LOCK TABLES `gorp_migrations` WRITE;
/*!40000 ALTER TABLE `gorp_migrations` DISABLE KEYS */;
INSERT INTO `gorp_migrations` VALUES ('20190705190058-create-users-table.sql','2022-12-22 20:04:58'),('20190708185420-create-operations-table.sql','2022-12-22 20:04:58'),('20190708185427-create-events-table.sql','2022-12-22 20:04:58'),('20190708185432-create-evidence-table.sql','2022-12-22 20:04:58'),('20190708185441-create-evidence-event-map-table.sql','2022-12-22 20:04:58'),('20190716190100-create-user-operation-map-table.sql','2022-12-22 20:04:58'),('20190722193434-create-tags-table.sql','2022-12-22 20:04:58'),('20190722193937-create-tag-event-map.sql','2022-12-22 20:04:58'),('20190909183500-add-short-name-to-users-table.sql','2022-12-22 20:04:58'),('20190909190416-add-short-name-index.sql','2022-12-22 20:04:58'),('20190926205116-evidence-name.sql','2022-12-22 20:04:59'),('20190930173342-add-saved-searches.sql','2022-12-22 20:04:59'),('20191001182541-evidence-tags.sql','2022-12-22 20:04:59'),('20191008005212-add-uuid-to-events-evidence.sql','2022-12-22 20:04:59'),('20191015235306-add-slug-to-operations.sql','2022-12-22 20:04:59'),('20191018172105-modular-auth.sql','2022-12-22 20:04:59'),('20191023170906-codeblock.sql','2022-12-22 20:04:59'),('20191101185207-replace-events-with-findings.sql','2022-12-22 20:04:59'),('20191114211948-add-operation-to-tags.sql','2022-12-22 20:04:59'),('20191205182830-create-api-keys-table.sql','2022-12-22 20:04:59'),('20191213222629-users-with-email.sql','2022-12-22 20:04:59'),('20200103194053-rename-short-name-to-slug.sql','2022-12-22 20:04:59'),('20200104013804-rework-ashirt-auth.sql','2022-12-22 20:04:59'),('20200116070736-add-admin-flag.sql','2022-12-22 20:04:59'),('20200130175541-fix-color-truncation.sql','2022-12-22 20:04:59'),('20200205200208-disable-user-support.sql','2022-12-22 20:04:59'),('20200215015330-optional-user-id.sql','2022-12-22 20:04:59'),('20200221195107-deletable-user.sql','2022-12-22 20:04:59'),('20200303215004-move-last-login.sql','2022-12-22 20:04:59'),('20200306221628-add-explicit-headless.sql','2022-12-22 20:04:59'),('20200331155258-finding-status.sql','2022-12-22 20:04:59'),('20200617193248-case-senitive-apikey.sql','2022-12-22 20:04:59'),('20200928160958-add-totp-secret-to-auth-table.sql','2022-12-22 20:04:59'),('20210120205510-create-email-queue-table.sql','2022-12-22 20:04:59'),('20210401220807-dynamic-categories.sql','2022-12-22 20:04:59'),('20210408212206-remove-findings-category.sql','2022-12-22 20:05:00'),('20210730170543-add-auth-type.sql','2022-12-22 20:05:00'),('20220211181557-add-default-tags.sql','2022-12-22 20:05:00'),('20220512174013-evidence-metadata.sql','2022-12-22 20:05:00'),('20220516163424-add-worker-services.sql','2022-12-22 20:05:00'),('20220811153414-webauthn-credentials.sql','2022-12-22 20:05:00'),('20220908193523-switch-to-username.sql','2022-12-22 20:05:00'),('20220912185024-add-is_favorite.sql','2022-12-22 20:05:00'),('20220916190855-remove-null-as-value-for-is_favorite.sql','2022-12-22 20:05:00'),('20221027152757-remove-operation-status.sql','2022-12-22 20:05:00'),('20221111221242-create-user-operation-preferences.sql','2022-12-22 20:05:00'),('20221121165342-add-groups.sql','2022-12-22 20:05:00'),('20221216195811-add-user-group-permissions-table.sql','2022-12-22 20:05:00');
INSERT INTO `gorp_migrations` VALUES ('20190705190058-create-users-table.sql','2023-03-24 12:59:29'),('20190708185420-create-operations-table.sql','2023-03-24 12:59:29'),('20190708185427-create-events-table.sql','2023-03-24 12:59:29'),('20190708185432-create-evidence-table.sql','2023-03-24 12:59:29'),('20190708185441-create-evidence-event-map-table.sql','2023-03-24 12:59:29'),('20190716190100-create-user-operation-map-table.sql','2023-03-24 12:59:29'),('20190722193434-create-tags-table.sql','2023-03-24 12:59:29'),('20190722193937-create-tag-event-map.sql','2023-03-24 12:59:29'),('20190909183500-add-short-name-to-users-table.sql','2023-03-24 12:59:29'),('20190909190416-add-short-name-index.sql','2023-03-24 12:59:29'),('20190926205116-evidence-name.sql','2023-03-24 12:59:29'),('20190930173342-add-saved-searches.sql','2023-03-24 12:59:29'),('20191001182541-evidence-tags.sql','2023-03-24 12:59:29'),('20191008005212-add-uuid-to-events-evidence.sql','2023-03-24 12:59:29'),('20191015235306-add-slug-to-operations.sql','2023-03-24 12:59:29'),('20191018172105-modular-auth.sql','2023-03-24 12:59:29'),('20191023170906-codeblock.sql','2023-03-24 12:59:29'),('20191101185207-replace-events-with-findings.sql','2023-03-24 12:59:30'),('20191114211948-add-operation-to-tags.sql','2023-03-24 12:59:30'),('20191205182830-create-api-keys-table.sql','2023-03-24 12:59:30'),('20191213222629-users-with-email.sql','2023-03-24 12:59:30'),('20200103194053-rename-short-name-to-slug.sql','2023-03-24 12:59:30'),('20200104013804-rework-ashirt-auth.sql','2023-03-24 12:59:30'),('20200116070736-add-admin-flag.sql','2023-03-24 12:59:30'),('20200130175541-fix-color-truncation.sql','2023-03-24 12:59:30'),('20200205200208-disable-user-support.sql','2023-03-24 12:59:30'),('20200215015330-optional-user-id.sql','2023-03-24 12:59:30'),('20200221195107-deletable-user.sql','2023-03-24 12:59:30'),('20200303215004-move-last-login.sql','2023-03-24 12:59:30'),('20200306221628-add-explicit-headless.sql','2023-03-24 12:59:30'),('20200331155258-finding-status.sql','2023-03-24 12:59:30'),('20200617193248-case-senitive-apikey.sql','2023-03-24 12:59:30'),('20200928160958-add-totp-secret-to-auth-table.sql','2023-03-24 12:59:30'),('20210120205510-create-email-queue-table.sql','2023-03-24 12:59:30'),('20210401220807-dynamic-categories.sql','2023-03-24 12:59:30'),('20210408212206-remove-findings-category.sql','2023-03-24 12:59:30'),('20210730170543-add-auth-type.sql','2023-03-24 12:59:30'),('20220211181557-add-default-tags.sql','2023-03-24 12:59:30'),('20220512174013-evidence-metadata.sql','2023-03-24 12:59:30'),('20220516163424-add-worker-services.sql','2023-03-24 12:59:30'),('20220811153414-webauthn-credentials.sql','2023-03-24 12:59:30'),('20220908193523-switch-to-username.sql','2023-03-24 12:59:30'),('20220912185024-add-is_favorite.sql','2023-03-24 12:59:30'),('20220916190855-remove-null-as-value-for-is_favorite.sql','2023-03-24 12:59:30'),('20221027152757-remove-operation-status.sql','2023-03-24 12:59:30'),('20221111221242-create-user-operation-preferences.sql','2023-03-24 12:59:30'),('20221121165342-add-groups.sql','2023-03-24 12:59:30'),('20221216195811-add-user-group-permissions-table.sql','2023-03-24 12:59:30'),('20230324124303-add-authn-id.sql','2023-03-24 12:59:31');
/*!40000 ALTER TABLE `gorp_migrations` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
Expand All @@ -526,4 +527,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2022-12-22 20:05:00
-- Dump completed on 2023-03-24 12:59:30
29 changes: 23 additions & 6 deletions frontend/src/authschemes/webauthn/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,24 @@ export const base64UrlEncode = (value: string) => {

export const toKeycode = (c: string) => c.charCodeAt(0)

export const toByteArray = (s: string) => Uint8Array.from(atob(s), toKeycode)
const base64UrlToBase64 = (input: string) => {
// Replace non-url compatible chars with base64 standard chars
let standardCharInput = input
.replace(/-/g, '+')
.replace(/_/g, '/');

// Pad out with standard base64 required padding characters
const pad = standardCharInput.length % 4;
if(pad) {
if(pad === 1) {
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
}
standardCharInput += new Array(5-pad).join('=');
}
return standardCharInput;
}

export const toByteArrayFromB64URL = (s: string) => Uint8Array.from(atob(base64UrlToBase64(s)), toKeycode)

export const convertToCredentialCreationOptions = (
input: ProvidedCredentialCreationOptions
Expand All @@ -30,13 +47,13 @@ export const convertToCredentialCreationOptions = (
...input,
publicKey: {
...input.publicKey,
challenge: toByteArray(input.publicKey.challenge),
challenge: toByteArrayFromB64URL(input.publicKey.challenge),
user: {
...input.publicKey.user,
id: toByteArray(input.publicKey.user.id)
id: toByteArrayFromB64URL(input.publicKey.user.id)
},
excludeCredentials: input.publicKey.excludeCredentials?.map(
cred => ({ ...cred, id: toByteArray(cred.id) })
cred => ({ ...cred, id: toByteArrayFromB64URL(cred.id) })
)
}
}
Expand All @@ -47,9 +64,9 @@ export const convertToCredentialCreationOptions = (
export const convertToPublicKeyCredentialRequestOptions = (input: ProvidedCredentialRequestOptions): PublicKeyCredentialRequestOptions => {
const output: PublicKeyCredentialRequestOptions = {
...input.publicKey,
challenge: toByteArray(input.publicKey.challenge),
challenge: toByteArrayFromB64URL(input.publicKey.challenge),
allowCredentials: input.publicKey.allowCredentials.map(
listItem => ({ ...listItem, id: toByteArray(listItem.id) })
listItem => ({ ...listItem, id: toByteArrayFromB64URL(listItem.id) })
)
}

Expand Down
Loading