Skip to content

Commit 0ceec03

Browse files
committed
audit tokens
1 parent 2d2a00b commit 0ceec03

File tree

12 files changed

+91
-33
lines changed

12 files changed

+91
-33
lines changed

model/server_settings.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type ServerSettings struct {
2727
KeyStorage FileStorageSettings `yaml:"keyStorage" json:"key_storage"`
2828
Config FileStorageSettings `yaml:"-" json:"config"`
2929
Logger LoggerSettings `yaml:"logger" json:"logger"`
30+
Audit AuditSettings `yaml:"audit" json:"audit"`
3031
AdminPanel AdminPanelSettings `yaml:"adminPanel" json:"admin_panel"`
3132
LoginWebApp FileStorageSettings `yaml:"loginWebApp" json:"login_web_app"`
3233
EmailTemplates FileStorageSettings `yaml:"emailTemplates" json:"email_templates"`
@@ -388,6 +389,18 @@ func HTTPLogDetailing(dumpRequest bool, logType HTTPDetailing) HTTPDetailing {
388389
return logType
389390
}
390391

392+
type TokenRecording string
393+
394+
const (
395+
TokenRecordingNone TokenRecording = "none"
396+
TokenRecordingObfuscated TokenRecording = "obfuscated"
397+
TokenRecordingFull TokenRecording = "full"
398+
)
399+
400+
type AuditSettings struct {
401+
TokenRecording TokenRecording `yaml:"tokenRecording" json:"tokenRecording"`
402+
}
403+
391404
type AdminPanelSettings struct {
392405
Enabled bool `json:"enabled" yaml:"enabled"`
393406
}

web/api/2fa.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,9 @@ func (ar *Router) FinalizeTFA() http.HandlerFunc {
306306
}
307307
}
308308

309-
ar.journal(JournalOperationLoginWith2FA,
310-
user.ID, app.ID, r.UserAgent(), user.AccessRole, scopes.Scopes())
309+
ar.audit(AuditOperationLoginWith2FA,
310+
user.ID, app.ID, r.UserAgent(), user.AccessRole, scopes.Scopes(),
311+
result.AccessToken, result.RefreshToken)
311312

312313
ar.server.Storages().User.UpdateLoginMetadata(user.ID)
313314
ar.ServeJSON(w, locale, http.StatusOK, result)

web/api/federated_login.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,9 @@ func (ar *Router) FederatedLoginComplete() http.HandlerFunc {
212212
authResult.CallbackUrl = fsess.CallbackUrl
213213
authResult.Scopes = fsess.Scopes
214214

215-
ar.journal(JournalOperationFederatedLogin,
216-
user.ID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes())
215+
ar.audit(AuditOperationFederatedLogin,
216+
user.ID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes(),
217+
authResult.AccessToken, authResult.RefreshToken)
217218

218219
ar.ServeJSON(w, locale, http.StatusOK, authResult)
219220
}

web/api/federated_oidc_login.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ func (ar *Router) OIDCLoginComplete(useSession bool) http.HandlerFunc {
249249
authResult.Scopes = resultScopes.Scopes()
250250
authResult.ProviderData = *providerData
251251

252-
ar.journal(JournalOperationOIDCLogin,
253-
user.ID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes())
252+
ar.audit(AuditOperationOIDCLogin,
253+
user.ID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes(),
254+
authResult.AccessToken, authResult.RefreshToken)
254255

255256
ar.ServeJSON(w, locale, http.StatusOK, authResult)
256257
}

web/api/impersonate_as.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ func (ar *Router) ImpersonateAs() http.HandlerFunc {
7777
// do not allow refresh for impersonated user
7878
authResult.RefreshToken = ""
7979

80-
ar.journal(JournalOperationImpersonatedAs,
81-
userID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes())
80+
ar.audit(AuditOperationImpersonatedAs,
81+
userID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes(),
82+
authResult.AccessToken, authResult.RefreshToken)
8283

8384
ar.ServeJSON(w, locale, http.StatusOK, authResult)
8485
}

web/api/journal.go

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,36 @@ package api
22

33
import (
44
"github.com/madappgang/identifo/v2/logging"
5+
"github.com/madappgang/identifo/v2/model"
56
)
67

7-
type JournalOperation string
8+
type AuditOperation string
89

910
const (
10-
JournalOperationLoginWithPassword JournalOperation = "login_with_password"
11-
JournalOperationLoginWithPhone JournalOperation = "login_with_phone"
12-
JournalOperationLoginWith2FA JournalOperation = "login_with_2fa"
13-
JournalOperationRefreshToken JournalOperation = "refresh_token"
14-
JournalOperationOIDCLogin JournalOperation = "oidc_login"
15-
JournalOperationFederatedLogin JournalOperation = "federated_login"
16-
JournalOperationRegistration JournalOperation = "registration"
17-
JournalOperationLogout JournalOperation = "logout"
18-
JournalOperationImpersonatedAs JournalOperation = "impersonated_as"
11+
AuditOperationLoginWithPassword AuditOperation = "login_with_password"
12+
AuditOperationLoginWithPhone AuditOperation = "login_with_phone"
13+
AuditOperationLoginWith2FA AuditOperation = "login_with_2fa"
14+
AuditOperationRefreshToken AuditOperation = "refresh_token"
15+
AuditOperationOIDCLogin AuditOperation = "oidc_login"
16+
AuditOperationFederatedLogin AuditOperation = "federated_login"
17+
AuditOperationRegistration AuditOperation = "registration"
18+
AuditOperationLogout AuditOperation = "logout"
19+
AuditOperationImpersonatedAs AuditOperation = "impersonated_as"
1920
)
2021

21-
func (ar *Router) journal(
22-
op JournalOperation,
22+
func (ar *Router) audit(
23+
op AuditOperation,
2324
userID, appID, device, accessRole string,
2425
scopes []string,
26+
accessToken, refreshToken string,
2527
) {
2628
iss := ar.server.Services().Token.Issuer()
2729

30+
auditSettings := ar.server.Settings().Audit
31+
32+
accessToken = maskToken(accessToken, auditSettings.TokenRecording)
33+
refreshToken = maskToken(refreshToken, auditSettings.TokenRecording)
34+
2835
// TODO: Create an interface for the audit log
2936
// Implement it for logging to stdout, a database, or a remote service
3037
ar.logger.Info("audit_record",
@@ -35,5 +42,24 @@ func (ar *Router) journal(
3542
"issuer", iss,
3643
"accessRole", accessRole,
3744
"scopes", scopes,
45+
"accessToken", accessToken,
46+
"refreshToken", refreshToken,
3847
)
3948
}
49+
50+
func maskToken(token string, tokenRecording model.TokenRecording) string {
51+
switch tokenRecording {
52+
case model.TokenRecordingNone:
53+
return "<redacted>"
54+
case model.TokenRecordingObfuscated:
55+
if len(token) < 32 {
56+
return "<short>"
57+
}
58+
59+
return token[:6] + "..." + token[len(token)-6:]
60+
case model.TokenRecordingFull:
61+
return token
62+
default:
63+
return "<redacted>"
64+
}
65+
}

web/api/login.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ func (ar *Router) LoginWithPassword() http.HandlerFunc {
189189
return
190190
}
191191

192-
ar.journal(JournalOperationLoginWithPassword,
193-
user.ID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes())
192+
ar.audit(AuditOperationLoginWithPassword,
193+
user.ID, app.ID, r.UserAgent(), user.AccessRole, resultScopes.Scopes(),
194+
authResult.AccessToken, authResult.RefreshToken)
194195

195196
ar.ServeJSON(w, locale, http.StatusOK, authResult)
196197
}

web/api/logout.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ func (ar *Router) Logout() http.HandlerFunc {
6464
}
6565
}
6666

67-
ar.journal(JournalOperationLogout,
68-
accessToken.Subject(), accessToken.Audience(), r.UserAgent(), "", nil)
67+
ar.audit(AuditOperationLogout,
68+
accessToken.Subject(), accessToken.Audience(), r.UserAgent(), "", nil,
69+
accessTokenString, d.RefreshToken)
6970

7071
ar.ServeJSON(w, locale, http.StatusOK, result)
7172
}

web/api/phone_login.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,9 @@ func (ar *Router) PhoneLogin() http.HandlerFunc {
169169
User: user,
170170
}
171171

172-
ar.journal(JournalOperationLoginWithPhone,
173-
user.ID, app.ID, r.UserAgent(), user.AccessRole, scopes.Scopes())
172+
ar.audit(AuditOperationLoginWithPhone,
173+
user.ID, app.ID, r.UserAgent(), user.AccessRole, scopes.Scopes(),
174+
result.AccessToken, result.RefreshToken)
174175

175176
ar.server.Storages().User.UpdateLoginMetadata(user.ID)
176177

@@ -208,7 +209,7 @@ func (l *PhoneLogin) validateCodeAndPhone() error {
208209

209210
func (l *PhoneLogin) validatePhone() error {
210211
if !model.PhoneRegexp.MatchString(l.PhoneNumber) {
211-
return errors.New("ohone number is not valid")
212+
return errors.New("phone number is not valid")
212213
}
213214
return nil
214215
}

web/api/refresh_token.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ func (ar *Router) RefreshTokens() http.HandlerFunc {
8989
}
9090

9191
resultScopes := strings.Split(accessToken.Scopes(), " ")
92-
ar.journal(JournalOperationRefreshToken,
93-
oldRefreshToken.Subject(), app.ID, r.UserAgent(), "", resultScopes)
92+
ar.audit(AuditOperationRefreshToken,
93+
oldRefreshToken.Subject(), app.ID, r.UserAgent(), "", resultScopes,
94+
result.AccessToken, result.RefreshToken)
9495

9596
ar.ServeJSON(w, locale, http.StatusOK, result)
9697
}

0 commit comments

Comments
 (0)