Skip to content

Commit

Permalink
Merge pull request #124 from MadAppGang/auh_callback
Browse files Browse the repository at this point in the history
Auh callback
  • Loading branch information
erudenko authored Mar 26, 2021
2 parents 2dc138a + fe91e82 commit f580767
Show file tree
Hide file tree
Showing 23 changed files with 540 additions and 192 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"port": 2345,
"host": "127.0.0.1",
"program": "${workspaceFolder}/cmd/boltdb/main.go",
"cwd": "${workspaceFolder}/cmd/boltdb/",
"cwd": "${workspaceFolder}",
"env": {
"SES_REGION": "us-east-1",
"SES_SENDER": "[email protected]",
Expand Down
4 changes: 2 additions & 2 deletions cmd/boltdb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (

const (
testAppID = "59fd884d8f6b180001f5b4e2"
appsImportPath = "../import/apps.json"
usersImportPath = "../import/users.json"
appsImportPath = "./cmd/import/apps.json"
usersImportPath = "./cmd/import/users.json"
)

func initServer() model.Server {
Expand Down
15 changes: 10 additions & 5 deletions jwt/service/jwt_token_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (ts *JWTokenService) ValidateTokenString(tstr string, v jwtValidator.Valida
}

// NewAccessToken creates new access token for user.
func (ts *JWTokenService) NewAccessToken(u model.User, scopes []string, app model.AppData, requireTFA bool) (ijwt.Token, error) {
func (ts *JWTokenService) NewAccessToken(u model.User, scopes []string, app model.AppData, requireTFA bool, tokenPayload map[string]interface{}) (ijwt.Token, error) {
if !app.Active() {
return nil, ErrInvalidApp
}
Expand All @@ -168,7 +168,7 @@ func (ts *JWTokenService) NewAccessToken(u model.User, scopes []string, app mode
return nil, ErrInvalidUser
}

payload := make(map[string]string)
payload := make(map[string]interface{})
if contains(app.TokenPayload(), PayloadName) {
payload[PayloadName] = u.Username()
}
Expand All @@ -177,6 +177,11 @@ func (ts *JWTokenService) NewAccessToken(u model.User, scopes []string, app mode
if requireTFA {
scopes = []string{model.TokenTFAPreauthScope}
}
if len(tokenPayload) > 0 {
for k, v := range tokenPayload {
payload[k] = v
}
}

now := ijwt.TimeFunc().Unix()

Expand Down Expand Up @@ -229,7 +234,7 @@ func (ts *JWTokenService) NewRefreshToken(u model.User, scopes []string, app mod
return nil, ErrInvalidUser
}

payload := make(map[string]string)
payload := make(map[string]interface{})
if contains(app.TokenPayload(), PayloadName) {
payload[PayloadName] = u.Username()
}
Expand Down Expand Up @@ -306,7 +311,7 @@ func (ts *JWTokenService) RefreshAccessToken(refreshToken ijwt.Token) (ijwt.Toke
return nil, ErrInvalidUser
}

token, err := ts.NewAccessToken(user, strings.Split(claims.Scopes, " "), app, false)
token, err := ts.NewAccessToken(user, strings.Split(claims.Scopes, " "), app, false, nil)
if err != nil {
return nil, err
}
Expand All @@ -324,7 +329,7 @@ func (ts *JWTokenService) RefreshAccessToken(refreshToken ijwt.Token) (ijwt.Toke

// NewInviteToken creates new invite token.
func (ts *JWTokenService) NewInviteToken(email string) (ijwt.Token, error) {
payload := make(map[string]string)
payload := make(map[string]interface{})
// add payload data here
if email != "" {
payload["email"] = email
Expand Down
2 changes: 1 addition & 1 deletion jwt/service/token_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const (

// TokenService is an abstract token manager.
type TokenService interface {
NewAccessToken(u model.User, scopes []string, app model.AppData, requireTFA bool) (ijwt.Token, error)
NewAccessToken(u model.User, scopes []string, app model.AppData, requireTFA bool, tokenPayload map[string]interface{}) (ijwt.Token, error)
NewRefreshToken(u model.User, scopes []string, app model.AppData) (ijwt.Token, error)
RefreshAccessToken(token ijwt.Token) (ijwt.Token, error)
NewInviteToken(email string) (ijwt.Token, error)
Expand Down
14 changes: 7 additions & 7 deletions jwt/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Token interface {
UserID() string
Type() string
Scopes() string
Payload() map[string]string
Payload() map[string]interface{}
}

// NewTokenWithClaims generates new JWT token with claims and keyID.
Expand Down Expand Up @@ -68,10 +68,10 @@ func (t *JWToken) UserID() string {
}

// Payload returns token payload.
func (t *JWToken) Payload() map[string]string {
func (t *JWToken) Payload() map[string]interface{} {
claims, ok := t.JWT.Claims.(*Claims)
if !ok {
return make(map[string]string)
return make(map[string]interface{})
}
return claims.Payload
}
Expand Down Expand Up @@ -159,10 +159,10 @@ func (t *JWToken) Scopes() string {

// Claims is an extended claims structure.
type Claims struct {
Payload map[string]string `json:"payload,omitempty"`
Scopes string `json:"scopes,omitempty"`
Type string `json:"type,omitempty"`
KeyID string `json:"kid,omitempty"` // optional keyID
Payload map[string]interface{} `json:"payload,omitempty"`
Scopes string `json:"scopes,omitempty"`
Type string `json:"type,omitempty"`
KeyID string `json:"kid,omitempty"` // optional keyID
jwt.StandardClaims
}

Expand Down
5 changes: 2 additions & 3 deletions jwt/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ func TestTokenToString(t *testing.T) {
if !reflect.DeepEqual(claims1, claims2) {
t.Errorf("Claims = %+v, want %+v", claims1, claims2)
}

}

func TestNewToken(t *testing.T) {
Expand Down Expand Up @@ -253,14 +252,14 @@ func TestNewToken(t *testing.T) {
}
ustg, _ := mem.NewUserStorage()
user, _ := ustg.UserByNamePassword("username", "password")
//generate random user until we get active user
// generate random user until we get active user
for !user.Active() {
user, _ = ustg.UserByNamePassword("username", "password")
}
scopes := []string{"scope1", "scope2"}
tokenPayload := []string{"name"}
app := mem.MakeAppData("123456", "1", true, "testName", "testDescriprion", scopes, true, []string{}, 0, 0, 0, tokenPayload, true, true, model.TFAStatusDisabled, "", model.NoAuthz, "", "", []string{}, []string{}, "user")
token, err := ts.NewAccessToken(user, scopes, &app, false)
token, err := ts.NewAccessToken(user, scopes, &app, false, nil)
if err != nil {
t.Errorf("Unable to create token %v", err)
}
Expand Down
28 changes: 28 additions & 0 deletions model/app_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ type AppData interface {
NewUserDefaultRole() string
AppleInfo() *AppleInfo
SetSecret(secret string)

//Token payload related services
TokenPayloadService() TokenPayloadServiceType
TokenPayloadServicePluginSettings() TokenPayloadServicePluginSettings
TokenPayloadServiceHttpSettings() TokenPayloadServiceHttpSettings
}

// AppType is a type of application.
Expand Down Expand Up @@ -95,3 +100,26 @@ const (
// TFAStatusDisabled is when the app does not support TFA.
TFAStatusDisabled = "disabled"
)

// TokenPayloadServiceType service to allow fetch additional data to include to access token
type TokenPayloadServiceType string

const (
// TokenPayloadServiceNone no service is used
TokenPayloadServiceNone = "none"
// TokenPayloadServicePlugin user local identifo plugin with specific name to retreive token payload
TokenPayloadServicePlugin = "plugin"
// TokenPayloadServiceHttp use external service to get token paylad
TokenPayloadServiceHttp = "http"
)

// TokenPayloadServicePluginSettings settings for token payload service
type TokenPayloadServicePluginSettings struct {
Name string `json:"name,omitempty" bson:"name,omitempty"`
}

// TokenPayloadServiceHttpSettings settings for token payload service
type TokenPayloadServiceHttpSettings struct {
URL string `json:"url,omitempty" bson:"url,omitempty"`
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
6 changes: 6 additions & 0 deletions model/user_data_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package model

//TokenPayloadProvider provides additional user payload to include to the token
type TokenPayloadProvider interface {
TokenPayloadForApp(appId, appName, userId string) (map[string]interface{}, error)
}
101 changes: 62 additions & 39 deletions storage/boltdb/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,55 @@ type AppData struct {
}

type appData struct {
ID string `json:"id,omitempty"`
Secret string `json:"secret,omitempty"`
Active bool `json:"active"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Offline bool `json:"offline"`
Type model.AppType `json:"type,omitempty"`
RedirectURLs []string `json:"redirect_urls,omitempty"`
RefreshTokenLifespan int64 `json:"refresh_token_lifespan,omitempty"`
InviteTokenLifespan int64 `json:"invite_token_lifespan,omitempty"`
TokenLifespan int64 `json:"token_lifespan,omitempty"`
TokenPayload []string `json:"token_payload,omitempty"`
RegistrationForbidden bool `json:"registration_forbidden"`
AnonymousRegistrationAllowed bool `json:"anonymous_registration_allowed"`
TFAStatus model.TFAStatus `json:"tfa_status"`
DebugTFACode string `json:"debug_tfa_code,omitempty"`
AuthorizationWay model.AuthorizationWay `json:"authorization_way,omitempty"`
AuthorizationModel string `json:"authorization_model,omitempty"`
AuthorizationPolicy string `json:"authorization_policy,omitempty"`
RolesWhitelist []string `json:"roles_whitelist,omitempty"`
RolesBlacklist []string `json:"roles_blacklist,omitempty"`
NewUserDefaultRole string `json:"new_user_default_role,omitempty"`
AppleInfo *model.AppleInfo `json:"apple_info,omitempty"`
ID string `json:"id,omitempty"`
Secret string `json:"secret,omitempty"`
Active bool `json:"active,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Offline bool `json:"offline,omitempty"`
Type model.AppType `json:"type,omitempty"`
RedirectURLs []string `json:"redirect_urls,omitempty"`
RefreshTokenLifespan int64 `json:"refresh_token_lifespan,omitempty"`
InviteTokenLifespan int64 `json:"invite_token_lifespan,omitempty"`
TokenLifespan int64 `json:"token_lifespan,omitempty"`
TokenPayload []string `json:"token_payload,omitempty"`
RegistrationForbidden bool `json:"registration_forbidden,omitempty"`
AnonymousRegistrationAllowed bool `json:"anonymous_registration_allowed,omitempty"`
TFAStatus model.TFAStatus `json:"tfa_status"`
DebugTFACode string `json:"debug_tfa_code,omitempty"`
AuthorizationWay model.AuthorizationWay `json:"authorization_way,omitempty"`
AuthorizationModel string `json:"authorization_model,omitempty"`
AuthorizationPolicy string `json:"authorization_policy,omitempty"`
RolesWhitelist []string `json:"roles_whitelist"`
RolesBlacklist []string `json:"roles_blacklist,omitempty"`
NewUserDefaultRole string `json:"new_user_default_role,omitempty"`
AppleInfo *model.AppleInfo `json:"apple_info,omitempty"`
TokenPayloadService model.TokenPayloadServiceType `json:"token_payload_service"`
TokenPayloadServicePluginSettings model.TokenPayloadServicePluginSettings `json:"token_payload_service_plugin_settings,omitempty"`
TokenPayloadServiceHttpSettings model.TokenPayloadServiceHttpSettings `json:"token_payload_service_http_settings,omitempty"`
}

// NewAppData instantiates in-memory app data model from the general one.
func NewAppData(data model.AppData) AppData {
return AppData{appData: appData{
ID: data.ID(),
Secret: data.Secret(),
Active: data.Active(),
Name: data.Name(),
Description: data.Description(),
Scopes: data.Scopes(),
Offline: data.Offline(),
RedirectURLs: data.RedirectURLs(),
RefreshTokenLifespan: data.RefreshTokenLifespan(),
InviteTokenLifespan: data.InviteTokenLifespan(),
TokenLifespan: data.TokenLifespan(),
TokenPayload: data.TokenPayload(),
RegistrationForbidden: data.RegistrationForbidden(),
AnonymousRegistrationAllowed: data.AnonymousRegistrationAllowed(),
ID: data.ID(),
Secret: data.Secret(),
Active: data.Active(),
Name: data.Name(),
Description: data.Description(),
Scopes: data.Scopes(),
Offline: data.Offline(),
RedirectURLs: data.RedirectURLs(),
RefreshTokenLifespan: data.RefreshTokenLifespan(),
InviteTokenLifespan: data.InviteTokenLifespan(),
TokenLifespan: data.TokenLifespan(),
TokenPayload: data.TokenPayload(),
RegistrationForbidden: data.RegistrationForbidden(),
AnonymousRegistrationAllowed: data.AnonymousRegistrationAllowed(),
TokenPayloadService: data.TokenPayloadService(),
TokenPayloadServicePluginSettings: data.TokenPayloadServicePluginSettings(),
TokenPayloadServiceHttpSettings: data.TokenPayloadServiceHttpSettings(),
}}
}

Expand Down Expand Up @@ -94,6 +100,7 @@ func MakeAppData(id, secret string, active bool, name, description string, scope
RolesWhitelist: rolesWhitelist,
RolesBlacklist: rolesBlacklist,
NewUserDefaultRole: newUserDefaultRole,
TokenPayloadService: model.TokenPayloadServiceNone,
}}
}

Expand Down Expand Up @@ -145,7 +152,9 @@ func (ad *AppData) TokenPayload() []string { return ad.appData.TokenPayload }
func (ad *AppData) RegistrationForbidden() bool { return ad.appData.RegistrationForbidden }

// AnonymousRegistrationAllowed implements model.AppData interface.
func (ad *AppData) AnonymousRegistrationAllowed() bool { return ad.appData.AnonymousRegistrationAllowed }
func (ad *AppData) AnonymousRegistrationAllowed() bool {
return ad.appData.AnonymousRegistrationAllowed
}

// TFAStatus implements model.AppData interface.
func (ad *AppData) TFAStatus() model.TFAStatus { return ad.appData.TFAStatus }
Expand Down Expand Up @@ -174,6 +183,18 @@ func (ad *AppData) NewUserDefaultRole() string { return ad.appData.NewUserDefaul
// AppleInfo implements model.AppData interface.
func (ad *AppData) AppleInfo() *model.AppleInfo { return ad.appData.AppleInfo }

func (ad *AppData) TokenPayloadService() model.TokenPayloadServiceType {
return ad.appData.TokenPayloadService
}

func (ad *AppData) TokenPayloadServicePluginSettings() model.TokenPayloadServicePluginSettings {
return ad.appData.TokenPayloadServicePluginSettings
}

func (ad *AppData) TokenPayloadServiceHttpSettings() model.TokenPayloadServiceHttpSettings {
return ad.appData.TokenPayloadServiceHttpSettings
}

// SetSecret implements model.AppData interface.
func (ad *AppData) SetSecret(secret string) {
if ad == nil {
Expand All @@ -195,4 +216,6 @@ func (ad *AppData) Sanitize() {
ad.appData.AuthorizationWay = ""
ad.appData.AuthorizationModel = ""
ad.appData.AuthorizationPolicy = ""
ad.appData.TokenPayloadServiceHttpSettings = model.TokenPayloadServiceHttpSettings{}
ad.appData.TokenPayloadServicePluginSettings = model.TokenPayloadServicePluginSettings{}
}
Loading

0 comments on commit f580767

Please sign in to comment.