Skip to content

Commit 4bbedb5

Browse files
Foosecbcmmbaga
andauthored
[client] Add mTLS support for SSO login (#2188)
* Add mTLS support for SSO login * Refactor variable to follow Go naming conventions --------- Co-authored-by: bcmmbaga <[email protected]>
1 parent 9716be8 commit 4bbedb5

File tree

6 files changed

+51
-4
lines changed

6 files changed

+51
-4
lines changed

client/android/login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func (a *Auth) SaveConfigIfSSOSupported(listener SSOListener) {
8484
func (a *Auth) saveConfigIfSSOSupported() (bool, error) {
8585
supportsSSO := true
8686
err := a.withBackOff(a.ctx, func() (err error) {
87-
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
87+
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL, nil)
8888
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.NotFound || s.Code() == codes.Unimplemented) {
8989
_, err = internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
9090
s, ok := gstatus.FromError(err)

client/internal/auth/oauth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func NewOAuthFlow(ctx context.Context, config *internal.Config, isLinuxDesktopCl
8686

8787
// authenticateWithPKCEFlow initializes the Proof Key for Code Exchange flow auth flow
8888
func authenticateWithPKCEFlow(ctx context.Context, config *internal.Config) (OAuthFlow, error) {
89-
pkceFlowInfo, err := internal.GetPKCEAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL)
89+
pkceFlowInfo, err := internal.GetPKCEAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL, config.ClientCertKeyPair)
9090
if err != nil {
9191
return nil, fmt.Errorf("getting pkce authorization flow info failed with error: %v", err)
9292
}

client/internal/auth/pkce_flow.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/sha256"
66
"crypto/subtle"
7+
"crypto/tls"
78
"encoding/base64"
89
"errors"
910
"fmt"
@@ -143,6 +144,18 @@ func (p *PKCEAuthorizationFlow) WaitToken(ctx context.Context, _ AuthFlowInfo) (
143144
func (p *PKCEAuthorizationFlow) startServer(server *http.Server, tokenChan chan<- *oauth2.Token, errChan chan<- error) {
144145
mux := http.NewServeMux()
145146
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
147+
cert := p.providerConfig.ClientCertPair
148+
if cert != nil {
149+
tr := &http.Transport{
150+
TLSClientConfig: &tls.Config{
151+
Certificates: []tls.Certificate{*cert},
152+
},
153+
}
154+
sslClient := &http.Client{Transport: tr}
155+
ctx := context.WithValue(req.Context(), oauth2.HTTPClient, sslClient)
156+
req = req.WithContext(ctx)
157+
}
158+
146159
token, err := p.handleRequest(req)
147160
if err != nil {
148161
renderPKCEFlowTmpl(w, err)

client/internal/config.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package internal
22

33
import (
44
"context"
5+
"crypto/tls"
56
"fmt"
67
"net/url"
78
"os"
@@ -57,6 +58,8 @@ type ConfigInput struct {
5758
DisableAutoConnect *bool
5859
ExtraIFaceBlackList []string
5960
DNSRouteInterval *time.Duration
61+
ClientCertPath string
62+
ClientCertKeyPath string
6063
}
6164

6265
// Config Configuration type
@@ -102,6 +105,13 @@ type Config struct {
102105

103106
// DNSRouteInterval is the interval in which the DNS routes are updated
104107
DNSRouteInterval time.Duration
108+
//Path to a certificate used for mTLS authentication
109+
ClientCertPath string
110+
111+
//Path to corresponding private key of ClientCertPath
112+
ClientCertKeyPath string
113+
114+
ClientCertKeyPair *tls.Certificate `json:"-"`
105115
}
106116

107117
// ReadConfig read config file and return with Config. If it is not exists create a new with default values
@@ -385,6 +395,26 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
385395

386396
}
387397

398+
if input.ClientCertKeyPath != "" {
399+
config.ClientCertKeyPath = input.ClientCertKeyPath
400+
updated = true
401+
}
402+
403+
if input.ClientCertPath != "" {
404+
config.ClientCertPath = input.ClientCertPath
405+
updated = true
406+
}
407+
408+
if config.ClientCertPath != "" && config.ClientCertKeyPath != "" {
409+
cert, err := tls.LoadX509KeyPair(config.ClientCertPath, config.ClientCertKeyPath)
410+
if err != nil {
411+
log.Error("Failed to load mTLS cert/key pair: ", err)
412+
} else {
413+
config.ClientCertKeyPair = &cert
414+
log.Info("Loaded client mTLS cert/key pair")
415+
}
416+
}
417+
388418
return updated, nil
389419
}
390420

client/internal/pkce_auth.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package internal
22

33
import (
44
"context"
5+
"crypto/tls"
56
"fmt"
67
"net/url"
78

@@ -36,10 +37,12 @@ type PKCEAuthProviderConfig struct {
3637
RedirectURLs []string
3738
// UseIDToken indicates if the id token should be used for authentication
3839
UseIDToken bool
40+
//ClientCertPair is used for mTLS authentication to the IDP
41+
ClientCertPair *tls.Certificate
3942
}
4043

4144
// GetPKCEAuthorizationFlowInfo initialize a PKCEAuthorizationFlow instance and return with it
42-
func GetPKCEAuthorizationFlowInfo(ctx context.Context, privateKey string, mgmURL *url.URL) (PKCEAuthorizationFlow, error) {
45+
func GetPKCEAuthorizationFlowInfo(ctx context.Context, privateKey string, mgmURL *url.URL, clientCert *tls.Certificate) (PKCEAuthorizationFlow, error) {
4346
// validate our peer's Wireguard PRIVATE key
4447
myPrivateKey, err := wgtypes.ParseKey(privateKey)
4548
if err != nil {
@@ -93,6 +96,7 @@ func GetPKCEAuthorizationFlowInfo(ctx context.Context, privateKey string, mgmURL
9396
Scope: protoPKCEAuthorizationFlow.GetProviderConfig().GetScope(),
9497
RedirectURLs: protoPKCEAuthorizationFlow.GetProviderConfig().GetRedirectURLs(),
9598
UseIDToken: protoPKCEAuthorizationFlow.GetProviderConfig().GetUseIDToken(),
99+
ClientCertPair: clientCert,
96100
},
97101
}
98102

client/ios/NetBirdSDK/login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (a *Auth) SaveConfigIfSSOSupported() (bool, error) {
7474
err := a.withBackOff(a.ctx, func() (err error) {
7575
_, err = internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
7676
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.NotFound || s.Code() == codes.Unimplemented) {
77-
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
77+
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL, nil)
7878
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.NotFound || s.Code() == codes.Unimplemented) {
7979
supportsSSO = false
8080
err = nil

0 commit comments

Comments
 (0)