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

Feature/OIDC #29

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ LICENSE
Makefile
NOTICE
README.md
configuration.md
arm/
powerpc/
mips/
Expand All @@ -19,3 +20,4 @@ node2
node3
.gitignore
changelog.md
scripts/
29 changes: 28 additions & 1 deletion badaas.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,31 @@ default:
admin:
# The admin password for the first run. Won't change is the admin user already exists.
password: admin


auth:
type: oidc # please note that the key auth.type is set to "oidc"
oidc:
# The Oauth2.0 Client ID
# (mandatory)
clientID: oidcCLIENT

# The Oauth2 Client Secret
# (mandatory)
clientSecret: abcd

# The issuer URL
# (mandatory)
issuer: "http://accounts.super-company.com"

# The redirect url. Use after the OIDC provider has authenticated the user.
# Probably should be set to redirect to the SPA.
# (mandatory)
redirectURL: "http://super-spa.com/auth/oidc/callback"

# The UNIQUE id claim used to identify OIDC users.
# Default to "sub", should not be changed unless you know what you are doing.
claimIdentifier: email

# The scopes needed to get the claims.
# Defaults to "", should not be changed unless you know what you are doing.
scopes: "profile,email"
36 changes: 29 additions & 7 deletions commands/init.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package commands

import (
"strings"

"github.com/ditrit/badaas/configuration"
"github.com/ditrit/badaas/persistence/models"
"github.com/ditrit/badaas/persistence/repository"
"github.com/ditrit/badaas/services/userservice"
"go.uber.org/zap"
)
Expand All @@ -15,13 +15,35 @@ func createSuperUser(
userService userservice.UserService,
) error {
// Create a super admin user and exit with code 1 on error
_, err := userService.NewUser("admin", "[email protected]", config.GetAdminPassword())
_, err := userService.NewUser("admin", "[email protected]", config.GetAdminPassword(), "")
if err != nil {
if err == repository.ErrDuplicateKey {
logger.Sugar().Infof("The superadmin user already exists in database")
return nil
}
logger.Sugar().Errorf("failed to save the super admin %w", err)
return err
}
return nil
}

// Create a OIDC user
func createOIDCUser(
logger *zap.Logger,
userService userservice.UserService,
userRepo repository.CRUDRepository[models.User, uint],
) error {
// Create a super admin user and exit with code 1 on error
_, err := userService.NewUser("John Doe", "[email protected]", "lqsjkdqsjnd", "[email protected]")
if err != nil {
if !strings.Contains(err.Error(), "already exist in database") {
logger.Sugar().Errorf("failed to save the super admin %w", err)
return err
if err == repository.ErrDuplicateKey {
logger.Sugar().Infof("The oidc user already exists in database")
return nil
}
logger.Sugar().Infof("The superadmin user already exists in database")
logger.Sugar().Errorf("failed to save the oidc user %w", err)
return err
}

logger.Sugar().Infof("The oidc user has been successfully created")
return nil
}
17 changes: 17 additions & 0 deletions commands/initAuthenticationCommands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package commands

import (
"github.com/ditrit/badaas/configuration"
"github.com/ditrit/verdeter"
"github.com/ditrit/verdeter/validators"
)

func initAuthenticationCommands(cfg *verdeter.VerdeterCommand) {
cfg.GKey(configuration.AuthTypeKey, verdeter.IsInt, "", "The type of authentication we want badaas to use.")
cfg.SetDefault(configuration.AuthTypeKey, string(configuration.AuthTypeNone))
cfg.AddValidator(configuration.AuthTypeKey, validators.AuthorizedValues("authorized values",
string(configuration.AuthTypePlain),
string(configuration.AuthTypeOIDC),
),
)
}
22 changes: 22 additions & 0 deletions commands/initOIDCCommands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package commands

import (
"github.com/ditrit/badaas/configuration"
"github.com/ditrit/verdeter"
)

func initOIDCCommands(cfg *verdeter.VerdeterCommand) {
cfg.LKey(configuration.OIDCClientIDKey, verdeter.IsStr, "", "The OIDC client ID provided by the OIDC Provider")

cfg.LKey(configuration.OIDCClientSecretIDKey, verdeter.IsStr, "", "The OIDC client secret provided by the OIDC Provider")

cfg.LKey(configuration.OIDCIssuerKey, verdeter.IsStr, "", "The OIDC issuer URL (example: https://accounts.google.com)")

cfg.LKey(configuration.OIDCClaimIdentifierKey, verdeter.IsStr, "",
"The name of the unique user identifier in the claims of the ID Token returned by the OIDC Provider.")
cfg.SetDefault(configuration.OIDCClaimIdentifierKey, "sub")

cfg.LKey(configuration.OIDCRedirectURLKey, verdeter.IsStr, "", "The URL of the callback on the SPA")

cfg.LKey(configuration.OIDCScopesKey, verdeter.IsStr, "", "The scopes to request to the OIDC Provider.")
}
8 changes: 4 additions & 4 deletions commands/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestCreateSuperUser(t *testing.T) {
initializationConfig.On("GetAdminPassword").Return("adminpassword")
userService := mockUserServices.NewUserService(t)
userService.
On("NewUser", "admin", "[email protected]", "adminpassword").
On("NewUser", "admin", "[email protected]", "adminpassword", "").
Return(nil, nil)
err := createSuperUser(
initializationConfig,
Expand All @@ -36,14 +36,14 @@ func TestCreateSuperUser_UserExists(t *testing.T) {
initializationConfig.On("GetAdminPassword").Return("adminpassword")
userService := mockUserServices.NewUserService(t)
userService.
On("NewUser", "admin", "[email protected]", "adminpassword").
On("NewUser", "admin", "[email protected]", "adminpassword", "").
Return(nil, errors.New("user already exist in database"))
err := createSuperUser(
initializationConfig,
logger,
userService,
)
assert.NoError(t, err)
assert.Error(t, err)

require.Equal(t, 1, logs.Len())
}
Expand All @@ -55,7 +55,7 @@ func TestCreateSuperUser_UserServiceError(t *testing.T) {
initializationConfig.On("GetAdminPassword").Return("adminpassword")
userService := mockUserServices.NewUserService(t)
userService.
On("NewUser", "admin", "[email protected]", "adminpassword").
On("NewUser", "admin", "[email protected]", "adminpassword", "").
Return(nil, errors.New("email not valid"))
err := createSuperUser(
initializationConfig,
Expand Down
86 changes: 0 additions & 86 deletions commands/r.md

This file was deleted.

5 changes: 5 additions & 0 deletions commands/rootCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ditrit/badaas/logger"
"github.com/ditrit/badaas/persistence"
"github.com/ditrit/badaas/router"
"github.com/ditrit/badaas/services/auth/protocols/oidcservice"
"github.com/ditrit/badaas/services/sessionservice"
"github.com/ditrit/badaas/services/userservice"
"github.com/ditrit/verdeter"
Expand All @@ -28,6 +29,7 @@ func runHTTPServer(cfg *verdeter.VerdeterCommand, args []string) error {

fx.Provide(userservice.NewUserService),
fx.Provide(sessionservice.NewSessionService),
fx.Provide(oidcservice.NewOIDCService),
// logger for fx
fx.WithLogger(func(logger *zap.Logger) fxevent.Logger {
return &fxevent.ZapLogger{Logger: logger}
Expand All @@ -38,6 +40,7 @@ func runHTTPServer(cfg *verdeter.VerdeterCommand, args []string) error {
// Finally: we invoke the newly created server
fx.Invoke(func(*http.Server) { /* we need this function to be empty*/ }),
fx.Invoke(createSuperUser),
fx.Invoke(createOIDCUser),
).Run()
return nil
}
Expand Down Expand Up @@ -65,4 +68,6 @@ func init() {
initDatabaseCommands(rootCfg)
initInitialisationCommands(rootCfg)
initSessionCommands(rootCfg)
initAuthenticationCommands(rootCfg)
initOIDCCommands(rootCfg)
}
53 changes: 52 additions & 1 deletion configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ The config file can be formated in any syntax that [github.com/spf13/viper](http
- [HTTP Server](#http-server)
- [Default values](#default-values)
- [Session management](#session-management)
- [Authentication](#authentication)
- [OIDC](#oidc)

## Database

Expand Down Expand Up @@ -124,7 +126,6 @@ Session are extended if the user made a request to badaas in the "roll duration"

Please see the diagram below to see what is the roll duration relative to the session duration.


```txt
| session duration |
|<----------------------------------------->|
Expand All @@ -148,3 +149,53 @@ session:
# Default (3600) equal to 1 hour
rollDuration: 3600
```

## Authentication

```yml
auth:
type: plain
```

Enable by configuration type of authentification you want to use:

- `plain`: Only email/password auth
- `oidc`: OIDC auth

### OIDC

To enable OIDC based authentication, you have to provide some common values such as your Oauth2 ClientID, Client Secret and the issuer url.

The redirect url should probably be set to a url on your front-end app, then the front-end will return the authentication code to Badaas.

Usualy, users are uniquely identified by the "sub" claim on the ID Token or on the claims for the OIDC UserInfo endpoint. Badaas can use another claim if you need to identify users based on that other claim. Note that it should be changed if you EXPLICITLY need to change the identifying claim. That claim may be protected by a scope, you can make badaas ask for that scope by using `auth.oidc.scopes` (use comma separated values).

```yml
auth:
type: oidc # please note that the key auth.type is set to "oidc"
oidc:
# The Oauth2.0 Client ID
# (mandatory)
clientID: oidcCLIENT

# The Oauth2 Client Secret
# (mandatory)
clientSecret: abcd

# The issuer URL
# (mandatory)
issuer: "http://accounts.super-company.com"

# The redirect url. Use after the OIDC provider has authenticated the user.
# Probably should be set to redirect to the SPA.
# (mandatory)
redirectURL: "http://super-spa.com/auth/oidc/callback"

# The UNIQUE id claim used to identify OIDC users.
# Default to "sub", should not be changed unless you know what you are doing.
claimIdentifier: email

# The scopes needed to get the claims.
# Defaults to "", should not be changed unless you know what you are doing.
scopes: "profile,email"
```
Loading