Skip to content

Commit

Permalink
ROKMETRO fixes (rokwire#679)
Browse files Browse the repository at this point in the history
* add app-env.json and update port

* Update app-env.json

* Update app-env.json

* update detect-secrets, update secrets baseline

* update secrets baseline

* update makefile versioning

* Update Makefile

Fix typo

* print version

* upgrade go to v1.20

* fix secrets

* initial webauthn implementation (in progress)

* refactor webauthn to handle credentials, update docs

* avoid creating inaccessible accounts

* fix webauthn registration issues, add webauthn test page

* fix webauthn login flow

* update changelog

* [rokwire#659] WebAuthn authentication (#7)

* initial webauthn implementation (in progress)

* refactor webauthn to handle credentials, update docs

* avoid creating inaccessible accounts

* fix webauthn registration issues, add webauthn test page

* fix webauthn login flow

* update changelog

* fix error handling

* fix login issues for mobile

* upgrade dependencies

* [rokwire#659] webauthn authentication (#8)

* initial webauthn implementation (in progress)

* refactor webauthn to handle credentials, update docs

* avoid creating inaccessible accounts

* fix webauthn registration issues, add webauthn test page

* fix webauthn login flow

* update changelog

* fix error handling

* fix login issues for mobile

* upgrade dependencies

* add webauthn to account check types

* add configs for authenticator selection to supported auth type params (#10)

* upgrade dependencies

* [rokwire#665] Decouple authentication and verification mechanisms (#13)

* add configs for authenticator selection to supported auth type params

* start adding verification types (contains errors) [rokwire#665]

* continue splitting auth and verification types [rokwire#665]

* finish implementing password auth type, start code verification type, add phone verifier interface [rokwire#665]

* finish refactoring identifier, auth types, start updating apis [rokwire#665]

* finish fixing errors [rokwire#665]

* fix passkey errors [rokwire#665]

* bug fixes, email with passkey not working because no params in email auth type

* update identifier impl and auth impl getters to better handle backwards compatibility (has errors)

* bug fixes, email and passkey not completing registration

* add json omitempty tags to credential structs

* better identifier type parsing

* passkeys using email and username identifiers working

* start fixing phone, passkey auth

* bug fixes for phone and passkey, better error messages

* simplify phone verifier interface

* phone auth type link working, add authCommunicationChannel interface to handle verification functions

* add ability to link webauthn credentials to accounts

* only set username if empty

* Change messages handling for verification

* remove commented blocks

* cleanup

* return verified auth types when cannot find account with username but not identifier

* bug fixes

* fix phone auth type docs

---------

Co-authored-by: Stephen Hurwit <[email protected]>
Co-authored-by: akshadpai <[email protected]>

* add missing verify email env var to app-env.json

* Auth-verify split fixes (#16)

* bug fixes

* update secrets baseline

* fix issues introduced by nullable device IDs

* fix username format

* update secrets baseline

* fix sign up bug

* disable request docs validation

* fix startup error for caching auth type

* fix pkce generation

* fix random string interface

* set user agent for oidc requests

* revert auth type changes

* revert core models, go mod, auth interface impl, remove phone verifier interface

* revert web package webauthn additions, update API docs

* fix storage files

* revert auth.go

---------

Co-authored-by: Stephen Hurwit <[email protected]>
Co-authored-by: Stephen Hurwit <[email protected]>
Co-authored-by: akshadpai <[email protected]>
  • Loading branch information
4 people authored Oct 6, 2023
1 parent d7308a4 commit 4eb98cc
Show file tree
Hide file tree
Showing 24 changed files with 222 additions and 293 deletions.
20 changes: 7 additions & 13 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,6 @@
},
{
"path": "detect_secrets.filters.heuristic.is_templated_secret"
},
{
"path": "detect_secrets.filters.regex.should_exclude_file",
"pattern": [
"go.sum"
]
}
],
"results": {
Expand Down Expand Up @@ -138,7 +132,7 @@
"filename": "core/auth/apis.go",
"hashed_secret": "394e3412459f79523e12e1fa95a4cf141ccff122",
"is_verified": false,
"line_number": 2096
"line_number": 2100
}
],
"core/auth/auth.go": [
Expand All @@ -147,28 +141,28 @@
"filename": "core/auth/auth.go",
"hashed_secret": "417355fe2b66baa6826739a6d8006ab2ddcf5186",
"is_verified": false,
"line_number": 154
"line_number": 151
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "bbdb97274c94b9605a766e317fca26186c34c510",
"hashed_secret": "a358987289cd70bbf50fb10acbcb9bff73c66df6",
"is_verified": false,
"line_number": 156
"line_number": 153
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "58f3388441fbce0e48aef2bf74413a6f43f6dc70",
"is_verified": false,
"line_number": 936
"line_number": 937
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "94a7f0195bbbd2260c4e4d02b6348fbcd90b2b30",
"is_verified": false,
"line_number": 2442
"line_number": 2441
}
],
"core/auth/auth_type_email.go": [
Expand Down Expand Up @@ -366,5 +360,5 @@
}
]
},
"generated_at": "2023-07-11T15:43:42Z"
"generated_at": "2023-10-06T19:34:36Z"
}
18 changes: 11 additions & 7 deletions core/auth/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (a *Auth) GetHost() string {
// ipAddress (string): Client's IP address
// deviceType (string): "mobile" or "web" or "desktop" etc
// deviceOS (*string): Device OS
// deviceID (string): Device ID
// deviceID (*string): Device ID
// authenticationType (string): Name of the authentication method for provided creds (eg. "email", "username", "illinois_oidc")
// creds (string): Credentials/JSON encoded credential structure defined for the specified auth type
// apiKey (string): API key to validate the specified app
Expand All @@ -74,14 +74,14 @@ func (a *Auth) GetHost() string {
// Params (interface{}): authType-specific set of parameters passed back to client
// State (string): login state used if account is enrolled in MFA
// MFA types ([]model.MFAType): list of MFA types account is enrolled in
func (a *Auth) Login(ipAddress string, deviceType string, deviceOS *string, deviceID string, authenticationType string, creds string, apiKey string,
func (a *Auth) Login(ipAddress string, deviceType string, deviceOS *string, deviceID *string, authenticationType string, creds string, apiKey string,
appTypeIdentifier string, orgID string, params string, clientVersion *string, profile model.Profile, privacy model.Privacy, preferences map[string]interface{},
username string, admin bool, l *logs.Log) (*string, *model.LoginSession, []model.MFAType, error) {
//TODO - analyse what should go in one transaction

//validate if the provided auth type is supported by the provided application and organization
authType, appType, appOrg, err := a.validateAuthType(authenticationType, appTypeIdentifier, orgID)
if err != nil {
if err != nil || authType == nil {
return nil, nil, nil, errors.WrapErrorAction(logutils.ActionValidate, model.TypeAuthType, nil, err)
}

Expand All @@ -98,6 +98,8 @@ func (a *Auth) Login(ipAddress string, deviceType string, deviceOS *string, devi
return nil, nil, nil, errors.WrapErrorData(logutils.StatusInvalid, model.TypeAPIKey, nil, err)
}

username = strings.TrimSpace(strings.ToLower(username))

anonymous := false
sub := ""

Expand Down Expand Up @@ -127,7 +129,6 @@ func (a *Auth) Login(ipAddress string, deviceType string, deviceOS *string, devi
accountAuthType, responseParams, mfaTypes, externalIDs, err = a.applyExternalAuthType(*authType, *appType, *appOrg, creds, params, clientVersion, profile, privacy, preferences, username, admin, l)
if err != nil {
return nil, nil, nil, errors.WrapErrorAction(logutils.ActionApply, typeExternalAuthType, logutils.StringArgs("user"), err)

}

sub = accountAuthType.Account.ID
Expand Down Expand Up @@ -942,6 +943,7 @@ func (a *Auth) UpdateCredential(accountID string, accountAuthTypeID string, para
if err != nil || authTypeCreds == nil {
return errors.WrapErrorAction(logutils.ActionValidate, "reset password", nil, err)
}

//Update the credential with new password
credential.Value = authTypeCreds
if err = a.storage.UpdateCredential(nil, credential); err != nil {
Expand Down Expand Up @@ -1008,7 +1010,7 @@ func (a *Auth) ResetForgotCredential(credsID string, resetCode string, params st
func (a *Auth) ForgotCredential(authenticationType string, appTypeIdentifier string, orgID string, apiKey string, identifier string, l *logs.Log) error {
//validate if the provided auth type is supported by the provided application and organization
authType, _, appOrg, err := a.validateAuthType(authenticationType, appTypeIdentifier, orgID)
if err != nil {
if err != nil || authType == nil || appOrg == nil {
return errors.WrapErrorAction(logutils.ActionValidate, model.TypeAuthType, nil, err)
}

Expand Down Expand Up @@ -1061,6 +1063,7 @@ func (a *Auth) ForgotCredential(authenticationType string, appTypeIdentifier str
if err != nil || authTypeCreds == nil {
return errors.WrapErrorAction(logutils.ActionValidate, "forgot password", nil, err)
}

//Update the credential with reset code and expiry
credential.Value = authTypeCreds
if err = a.storage.UpdateCredential(nil, credential); err != nil {
Expand All @@ -1073,7 +1076,7 @@ func (a *Auth) ForgotCredential(authenticationType string, appTypeIdentifier str
func (a *Auth) SendVerifyCredential(authenticationType string, appTypeIdentifier string, orgID string, apiKey string, identifier string, l *logs.Log) error {
//validate if the provided auth type is supported by the provided application and organization
authType, _, appOrg, err := a.validateAuthType(authenticationType, appTypeIdentifier, orgID)
if err != nil {
if err != nil || authType == nil || appOrg == nil {
return errors.WrapErrorAction(logutils.ActionValidate, model.TypeAuthType, nil, err)
}
//validate api key before making db calls
Expand All @@ -1085,6 +1088,7 @@ func (a *Auth) SendVerifyCredential(authenticationType string, appTypeIdentifier
if !authType.UseCredentials {
return errors.ErrorData(logutils.StatusInvalid, model.TypeAuthType, logutils.StringArgs("credential verification code"))
}

authImpl, err := a.getAuthTypeImpl(*authType)
if err != nil {
return errors.WrapErrorAction(logutils.ActionLoadCache, model.TypeAuthType, nil, err)
Expand Down Expand Up @@ -1696,7 +1700,7 @@ func (a *Auth) LinkAccountAuthType(accountID string, authenticationType string,

//validate if the provided auth type is supported by the provided application and organization
authType, appType, appOrg, err := a.validateAuthType(authenticationType, appTypeIdentifier, account.AppOrg.Organization.ID)
if err != nil {
if err != nil || authType == nil || appType == nil || appOrg == nil {
return nil, nil, errors.WrapErrorAction(logutils.ActionValidate, model.TypeAuthType, nil, err)
}

Expand Down
37 changes: 18 additions & 19 deletions core/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
"github.com/rokwire/core-auth-library-go/v3/tokenauth"
"golang.org/x/sync/syncmap"
"gopkg.in/go-playground/validator.v9"
"gopkg.in/gomail.v2"

"github.com/rokwire/logging-library-go/v2/errors"
"github.com/rokwire/logging-library-go/v2/logs"
Expand Down Expand Up @@ -107,9 +106,6 @@ type Auth struct {
profileBB ProfileBuildingBlock
identityBB IdentityBuildingBlock

emailFrom string
emailDialer *gomail.Dialer

cachedIdentityProviders *syncmap.Map //cache identityProviders
identityProvidersLock *sync.RWMutex

Expand All @@ -119,11 +115,14 @@ type Auth struct {
//delete sessions timer
deleteSessionsTimer *time.Timer
deleteSessionsTimerDone chan bool

version string
}

// NewAuth creates a new auth instance
func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authService *authservice.AuthService, storage Storage, emailer Emailer, minTokenExp *int64, maxTokenExp *int64, supportLegacySigs bool, twilioAccountSID string,
twilioToken string, twilioServiceSID string, profileBB ProfileBuildingBlock, smtpHost string, smtpPortNum int, smtpUser string, smtpPassword string, smtpFrom string, logger *logs.Logger) (*Auth, error) {
func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authService *authservice.AuthService, storage Storage, emailer Emailer, minTokenExp *int64,
maxTokenExp *int64, supportLegacySigs bool, twilioAccountSID string, twilioToken string, twilioServiceSID string, profileBB ProfileBuildingBlock,
smtpHost string, smtpPortNum int, smtpUser string, smtpPassword string, smtpFrom string, logger *logs.Logger, version string) (*Auth, error) {
if minTokenExp == nil {
var minTokenExpVal int64 = 5
minTokenExp = &minTokenExpVal
Expand All @@ -133,8 +132,6 @@ func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authServi
var maxTokenExpVal int64 = 60
maxTokenExp = &maxTokenExpVal
}
//maybe set up from config collection for diff types of auth
emailDialer := gomail.NewDialer(smtpHost, smtpPortNum, smtpUser, smtpPassword)

authTypes := map[string]authType{}
externalAuthTypes := map[string]externalAuthType{}
Expand All @@ -153,7 +150,7 @@ func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authServi
auth := &Auth{storage: storage, emailer: emailer, logger: logger, authTypes: authTypes, externalAuthTypes: externalAuthTypes, anonymousAuthTypes: anonymousAuthTypes,
serviceAuthTypes: serviceAuthTypes, mfaTypes: mfaTypes, authPrivKey: authPrivKey, ServiceRegManager: nil, serviceID: serviceID, host: host, minTokenExp: *minTokenExp,
maxTokenExp: *maxTokenExp, profileBB: profileBB, cachedIdentityProviders: cachedIdentityProviders, identityProvidersLock: identityProvidersLock,
apiKeys: apiKeys, apiKeysLock: apiKeysLock, deleteSessionsTimerDone: deleteSessionsTimerDone, emailDialer: emailDialer, emailFrom: smtpFrom}
apiKeys: apiKeys, apiKeysLock: apiKeysLock, deleteSessionsTimerDone: deleteSessionsTimerDone, version: version}

err := auth.storeCoreRegs()
if err != nil {
Expand All @@ -178,20 +175,23 @@ func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authServi

auth.SignatureAuth = signatureAuth

//Initialize auth types
// auth types
initUsernameAuth(auth)
initEmailAuth(auth)
initPhoneAuth(auth, twilioAccountSID, twilioToken, twilioServiceSID)
initFirebaseAuth(auth)
initAnonymousAuth(auth)
initSignatureAuth(auth)

// external auth types
initOidcAuth(auth)
initSamlAuth(auth)

// service auth types
initStaticTokenServiceAuth(auth)
initSignatureServiceAuth(auth)

// mfa types
initTotpMfa(auth)
initEmailMfa(auth)
initPhoneMfa(auth)
Expand Down Expand Up @@ -638,8 +638,8 @@ func (a *Auth) applyAuthType(authType model.AuthType, appOrg model.ApplicationOr
}
if isSignUp {
if admin {

return "", nil, nil, nil, errors.ErrorData(logutils.StatusInvalid, "sign up", &logutils.FieldArgs{"identifier": userIdentifier, "auth_type": authType.Code, "app_org_id": appOrg.ID, "admin": true}).SetStatus(utils.ErrorStatusNotAllowed)
return "", nil, nil, nil, errors.ErrorData(logutils.StatusInvalid, "sign up", &logutils.FieldArgs{"identifier": userIdentifier,
"auth_type": authType.Code, "app_org_id": appOrg.ID, "admin": true}).SetStatus(utils.ErrorStatusNotAllowed)
}
message, accountAuthType, err := a.applySignUp(authImpl, account, authType, appOrg, userIdentifier, creds, params, clientVersion,
regProfile, privacy, regPreferences, username, l)
Expand Down Expand Up @@ -682,9 +682,10 @@ func (a *Auth) checkCredentialVerified(authImpl authType, accountAuthType *model
if err != nil {
return errors.WrapErrorAction(logutils.ActionVerify, "credential verified", nil, err)
}

if !*verified {
//it is unverified
if !*expired {
if expired == nil || !*expired {
//not expired, just notify the client that it is "unverified"
return errors.ErrorData("unverified", "credential", nil).SetStatus(utils.ErrorStatusUnverified)
}
Expand Down Expand Up @@ -1025,7 +1026,7 @@ func (a *Auth) findAccountAuthType(account *model.Account, authType *model.AuthT
if accountAuthType.Credential != nil {
//populate credentials in accountAuthType
credential, err := a.storage.FindCredential(nil, accountAuthType.Credential.ID)
if err != nil {
if err != nil || credential == nil {
return nil, errors.WrapErrorAction(logutils.ActionFind, model.TypeCredential, nil, err)
}
credential.AuthType = *authType
Expand Down Expand Up @@ -1106,7 +1107,7 @@ func (a *Auth) clearExpiredSessions(identifier string, l *logs.Log) error {

func (a *Auth) applyLogin(anonymous bool, sub string, authType model.AuthType, appOrg model.ApplicationOrganization,
accountAuthType *model.AccountAuthType, appType model.ApplicationType, externalIDs map[string]string, ipAddress string, deviceType string,
deviceOS *string, deviceID string, clientVersion *string, params map[string]interface{}, state string, l *logs.Log) (*model.LoginSession, error) {
deviceOS *string, deviceID *string, clientVersion *string, params map[string]interface{}, state string, l *logs.Log) (*model.LoginSession, error) {

var err error
var loginSession *model.LoginSession
Expand Down Expand Up @@ -1186,7 +1187,7 @@ func (a *Auth) applyLogin(anonymous bool, sub string, authType model.AuthType, a
return loginSession, nil
}

func (a *Auth) createDevice(accountID string, deviceType string, deviceOS *string, deviceID string, l *logs.Log) (*model.Device, error) {
func (a *Auth) createDevice(accountID string, deviceType string, deviceOS *string, deviceID *string, l *logs.Log) (*model.Device, error) {
//id
idUUID, _ := uuid.NewUUID()
id := idUUID.String()
Expand Down Expand Up @@ -1627,8 +1628,7 @@ func (a *Auth) linkAccountAuthType(account model.Account, authType model.AuthTyp
}
}

credentialID, _ := uuid.NewUUID()
credID := credentialID.String()
credID := uuid.NewString()

//apply sign up
message, credentialValue, err := authImpl.signUp(authType, appOrg, creds, params, credID, l)
Expand Down Expand Up @@ -2061,7 +2061,6 @@ func (a *Auth) validateAuthType(authenticationType string, appTypeIdentifier str
applicationType, err := a.storage.FindApplicationType(appTypeIdentifier)
if err != nil {
return nil, nil, nil, errors.WrapErrorAction(logutils.ActionFind, model.TypeApplicationType, logutils.StringArgs(appTypeIdentifier), err)

}
if applicationType == nil {
return nil, nil, nil, errors.ErrorData(logutils.StatusMissing, model.TypeApplicationType, logutils.StringArgs(appTypeIdentifier))
Expand Down
7 changes: 4 additions & 3 deletions core/auth/auth_type_oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,14 +387,15 @@ func (a *oidcAuthImpl) loadOidcTokenWithParams(params map[string]string, oidcCon
"Content-Length": strconv.Itoa(len(data.Encode())),
}

client := &http.Client{}
req, err := http.NewRequest(http.MethodPost, tokenURI, strings.NewReader(data.Encode()))
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionCreate, logutils.TypeRequest, nil, err)
}
for k, v := range headers {
req.Header.Set(k, v)
}

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionSend, logutils.TypeRequest, nil, err)
Expand Down Expand Up @@ -434,13 +435,13 @@ func (a *oidcAuthImpl) loadOidcUserInfo(token *oidcToken, url string) ([]byte, e
return nil, errors.ErrorData(logutils.StatusMissing, "user info url", nil)
}

client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionCreate, logutils.TypeRequest, nil, err)
}
req.Header.Set("Authorization", fmt.Sprintf("%s %s", token.TokenType, token.AccessToken))

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionSend, logutils.TypeRequest, nil, err)
Expand Down Expand Up @@ -518,7 +519,7 @@ func initOidcAuth(auth *Auth) (*oidcAuthImpl, error) {

err := auth.registerExternalAuthType(oidc.authType, oidc)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionRegister, model.TypeAuthType, nil, err)
return nil, errors.WrapErrorAction(logutils.ActionRegister, typeExternalAuthType, nil, err)
}

return oidc, nil
Expand Down
14 changes: 0 additions & 14 deletions core/auth/auth_type_phone.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2022 Board of Trustees of the University of Illinois.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package auth

import (
Expand Down
2 changes: 1 addition & 1 deletion core/auth/auth_type_saml.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func initSamlAuth(auth *Auth) (*samlAuthImpl, error) {

err := auth.registerExternalAuthType(saml.authType, saml)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionRegister, model.TypeAuthType, nil, err)
return nil, errors.WrapErrorAction(logutils.ActionRegister, typeExternalAuthType, nil, err)
}

return saml, nil
Expand Down
Loading

0 comments on commit 4eb98cc

Please sign in to comment.