Skip to content

Commit

Permalink
Add OIDC support
Browse files Browse the repository at this point in the history
  • Loading branch information
tknie committed Nov 4, 2024
1 parent 9315a71 commit 5b35e84
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 0 deletions.
5 changes: 5 additions & 0 deletions auth/authenthicate.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func (service *AuthenticationServer) Authenticate(principal PrincipalInterface,
principal.AddRoles(DefaultRoles)
log.Log.Debugf("SQL database service name %s", service.Module)
return callDatabaseAuthenticate(service.Module, user, passwd)
case OIDCClientMethod:
log.Log.Debugf("Plugin database service name %s", service.Module)
return callbackOIDCAuthenticate(service, principal, user, passwd)
case PluginMethod:
log.Log.Debugf("Plugin database service name %s", service.Module)
return callbackPluginAuthenticate(service, principal, user, passwd)
Expand All @@ -75,6 +78,8 @@ func (authMethod Method) String() string {
return "OpenID"
case SQLDatabaseMethod:
return "SQL"
case OIDCClientMethod:
return "OIDC"
case PluginMethod:
return "Plugin"
case CallbackMethod:
Expand Down
8 changes: 8 additions & 0 deletions auth/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const (
SQLDatabaseMethod
// PluginMethod plugin method
PluginMethod
// OIDCClientMethod use OIDC client
OIDCClientMethod
// CallbackMethod callback method
CallbackMethod
)
Expand All @@ -69,6 +71,8 @@ func MethodType(s string) Method {
return SQLDatabaseMethod
case "ldap":
return LDAPMethod
case "oidc":
return OIDCClientMethod
case "plugin":
return PluginMethod
case "callback":
Expand All @@ -90,6 +94,10 @@ type AuthenticationServer struct {
Layer string `xml:"driver,attr" yaml:"driver,omitempty"`
AuthMethod Method `xml:"-" yaml:"-"`
Target string `xml:"target,omitempty" yaml:"target,omitempty"`
ClientID string `xml:"clientID,omitempty" yaml:"clientID,omitempty"`
ClientSecret string `xml:"clientSecret,omitempty" yaml:"clientSecret,omitempty"`
URL string `xml:"url,omitempty" yaml:"url,omitempty"`
RedirectURL string `xml:"redirectUrl,omitempty" yaml:"redirectUrl,omitempty"`
PasswordFile string `xml:"passwordFile,omitempty" yaml:"passwordFile,omitempty"`
LDAP []Source `xml:"LDAP,omitempty" yaml:"LDAP,omitempty"`
}
Expand Down
5 changes: 5 additions & 0 deletions auth/basicauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func BasicAuth(user string, pass string) (PrincipalInterface, error) {
return nil, err
}
evaluateRoles(principal)
_, err = GenerateJWToken(principal)
if err != nil {
log.Log.Errorf("Basic auth error... %v", err)
return nil, err
}
if log.IsDebugLevel() {
log.Log.Debugf("Create principal: %p", principal.Name)
}
Expand Down
10 changes: 10 additions & 0 deletions auth/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var (
// WebToken Web token configuration
type WebToken struct {
Comment string `xml:",comment" yaml:"-"`
OAuth2 bool `xml:"oauth2,attr" yaml:"oauth2,omitempty"`
IssuerName string `xml:"issuer,attr" yaml:"issuer,omitempty"`
Expirer string `xml:"expire,attr" yaml:"expire,omitempty"`
Encrypt bool `xml:"encrypt,attr" yaml:"encrypt,omitempty"`
Expand Down Expand Up @@ -218,6 +219,9 @@ func parseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {

// InitWebTokenJose2 initialize WebToken Jose.v2 token
func (webToken *WebToken) InitWebTokenJose2() error {
if webToken.OAuth2 == true {
return webToken.InitWebTokenOIDC()
}
switch {
case webToken == nil:
return services.NewError("SYS00031")
Expand Down Expand Up @@ -273,6 +277,9 @@ func (webToken *WebToken) GenerateJWToken(IAt string, principal PrincipalInterfa
if webToken == nil {
return "", fmt.Errorf("web token not configured properly")
}
if webToken.OAuth2 {
return webToken.GenerateOIDCToken(IAt, principal)
}
token, err := generateCallbackToken(IAt, principal)
if err == nil {
return token, err
Expand Down Expand Up @@ -365,6 +372,9 @@ func (webToken *WebToken) JWTContainsRoles(token string, scopes []string) (Princ
if log.IsDebugLevel() {
log.Log.Debugf("Has role scopes %#v", scopes)
}
if webToken.OAuth2 {
return webToken.OIDCContainsRoles(token, scopes)
}
if webToken.PassToken != "" && token == webToken.PassToken {
si := &SessionInfo{UUID: webToken.PassToken}
p := PrincipalCreater(si, "XXXX", "")
Expand Down
4 changes: 4 additions & 0 deletions auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ func InitLoginService(auth *Authentication) error {
case SQLDatabaseMethod:
services.ServerMessage("Authentication using SQL database")
RegisterTargetForAuth(s.Layer, s.Target, s.Module)
case OIDCClientMethod:
services.ServerMessage("Authentication using OIDC client")
InitOIDC(s)
case PluginMethod:
services.ServerMessage("Authentication using plugin database")
err := CallbackInit(s)
Expand Down Expand Up @@ -93,6 +96,7 @@ func RemoveLoginService(auth *Authentication) {
case FileMethod:
RemovePasswordFile(s.PasswordFile)
services.ServerMessage("Remove Authentication password file type")
case OIDCClientMethod:
default:
log.Log.Debugf("Remove of authentication type %s not possible", s.Type)
}
Expand Down
104 changes: 104 additions & 0 deletions auth/oidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2022-2023 Thorsten A. Knieling
*
* 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
*
*/

package auth

import (
"context"
"errors"
"fmt"

"github.com/coreos/go-oidc"
"github.com/tknie/log"
"golang.org/x/oauth2"
)

var oauth2Config *oauth2.Config
var provider *oidc.Provider

func InitOIDC(auth *AuthenticationServer) error {
if auth == nil {
return errors.New("no OIDC client config given")
}
if auth.ClientID == "" || auth.ClientSecret == "" || auth.URL == "" {
return errors.New("no OIDC client config details given")
}
var err error
provider, err = oidc.NewProvider(context.Background(), auth.URL)
if err != nil {
log.Log.Debugf("Provider error: %s", err)
return err
}

// Configure an OpenID Connect aware OAuth2 client.
oauth2Config = &oauth2.Config{
ClientID: auth.ClientID,
ClientSecret: auth.ClientSecret,
// RedirectURL: OIDCClient.RedirectURL,

// Discovery returns the OAuth2 endpoints.
Endpoint: provider.Endpoint(),

// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
return nil
}

// callbackOIDCAuthenticate authenticate user and password to OIDC client
func callbackOIDCAuthenticate(auth *AuthenticationServer, principal PrincipalInterface, userName, passwd string) error {
if oauth2Config == nil {
return errors.New("no OIDC configured")
}
token, err := oauth2Config.PasswordCredentialsToken(context.Background(), userName, passwd)
if err != nil {
return err
}
principal.SetRemote("XX")
principal.SetSession(token)
return nil
}

// InitWebTokenOIDC init web token for OIDC
func (webToken *WebToken) InitWebTokenOIDC() error {
return nil
}

// GenerateJWToken generate JWT token using golang Jose.v2
func (webToken *WebToken) GenerateOIDCToken(IAt string, principal PrincipalInterface) (tokenString string, err error) {
token, ok := principal.Session().(oauth2.Token)
if !ok {
return "", errors.New("token generate OIDC mismatch")
}
return token.AccessToken, nil
}

func (webToken *WebToken) OIDCContainsRoles(token string, scopes []string) (PrincipalInterface, error) {
verifier := provider.Verifier(&oidc.Config{ClientID: oauth2Config.ClientID})

// Parse and verify ID Token payload.
idToken, err := verifier.Verify(context.Background(), token)
if err != nil {
return nil, err
}
fmt.Println("Check token: " + token)
fmt.Printf("ID token %#v\n", idToken)

// Extract custom claims
var claims struct {
Email string `json:"email"`
Verified bool `json:"email_verified"`
}
if err := idToken.Claims(&claims); err != nil {
return nil, err
}
return nil, errors.New("OIDC not implemented")
}
44 changes: 44 additions & 0 deletions auth/oidc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2022-2023 Thorsten A. Knieling
*
* 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
*
*/

package auth

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestOIDCclient(t *testing.T) {

clientID := os.Getenv("SERVICE_OIDC_CLIENT")
if clientID == "" {
return
}

auth := &AuthenticationServer{
ClientID: os.Getenv("SERVICE_OIDC_CLIENT"),
ClientSecret: os.Getenv("SERVICE_OIDC_SECRET"),
URL: os.Getenv("SERVICE_OIDC_URL"),
RedirectURL: os.Getenv("SERVICE_OIDC_REDIRECTURL"),
}
err := InitOIDC(auth)
if !assert.NoError(t, err) {
return
}
username := os.Getenv("SERVICE_OIDC_USER")
password := os.Getenv("SERVICE_OIDC_PASSWORD")

principal := &testPrincipal{}
err = callbackOIDCAuthenticate(auth, principal, username, password)
assert.NoError(t, err)
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
)

require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.11.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-jose/go-jose/v4 v4.0.4
Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -59,6 +63,8 @@ github.com/msteinert/pam v1.1.0 h1:VhLun/0n0kQYxiRBJJvVpC2jR6d21SWJFjpvUVj20Kc=
github.com/msteinert/pam v1.1.0/go.mod h1:M4FPeAW8g2ITO68W8gACDz13NDJyOQM9IQsQhrR6TOI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k=
github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
Expand All @@ -68,6 +74,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
Expand Down Expand Up @@ -110,6 +117,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -154,6 +163,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down

0 comments on commit 5b35e84

Please sign in to comment.