2023-07-27 11:31:07 +02:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
gstatus "google.golang.org/grpc/status"
|
|
|
|
|
|
|
|
"github.com/netbirdio/netbird/client/internal"
|
|
|
|
)
|
|
|
|
|
|
|
|
// OAuthFlow represents an interface for authorization using different OAuth 2.0 flows
|
|
|
|
type OAuthFlow interface {
|
|
|
|
RequestAuthInfo(ctx context.Context) (AuthFlowInfo, error)
|
|
|
|
WaitToken(ctx context.Context, info AuthFlowInfo) (TokenInfo, error)
|
|
|
|
GetClientID(ctx context.Context) string
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTTPClient http client interface for API calls
|
|
|
|
type HTTPClient interface {
|
|
|
|
Do(req *http.Request) (*http.Response, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AuthFlowInfo holds information for the OAuth 2.0 authorization flow
|
|
|
|
type AuthFlowInfo struct {
|
|
|
|
DeviceCode string `json:"device_code"`
|
|
|
|
UserCode string `json:"user_code"`
|
|
|
|
VerificationURI string `json:"verification_uri"`
|
|
|
|
VerificationURIComplete string `json:"verification_uri_complete"`
|
|
|
|
ExpiresIn int `json:"expires_in"`
|
|
|
|
Interval int `json:"interval"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Claims used when validating the access token
|
|
|
|
type Claims struct {
|
|
|
|
Audience interface{} `json:"aud"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// TokenInfo holds information of issued access token
|
|
|
|
type TokenInfo struct {
|
|
|
|
AccessToken string `json:"access_token"`
|
|
|
|
RefreshToken string `json:"refresh_token"`
|
|
|
|
IDToken string `json:"id_token"`
|
|
|
|
TokenType string `json:"token_type"`
|
|
|
|
ExpiresIn int `json:"expires_in"`
|
|
|
|
UseIDToken bool `json:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTokenToUse returns either the access or id token based on UseIDToken field
|
|
|
|
func (t TokenInfo) GetTokenToUse() string {
|
|
|
|
if t.UseIDToken {
|
|
|
|
return t.IDToken
|
|
|
|
}
|
|
|
|
return t.AccessToken
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewOAuthFlow initializes and returns the appropriate OAuth flow based on the management configuration.
|
|
|
|
func NewOAuthFlow(ctx context.Context, config *internal.Config) (OAuthFlow, error) {
|
2023-08-17 14:10:03 +02:00
|
|
|
log.Debug("loading pkce authorization flow info")
|
2023-07-27 11:31:07 +02:00
|
|
|
|
2023-08-17 14:10:03 +02:00
|
|
|
pkceFlowInfo, err := internal.GetPKCEAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL)
|
2023-07-27 11:31:07 +02:00
|
|
|
if err == nil {
|
2023-08-17 14:10:03 +02:00
|
|
|
return NewPKCEAuthorizationFlow(pkceFlowInfo.ProviderConfig)
|
2023-07-27 11:31:07 +02:00
|
|
|
}
|
|
|
|
|
2023-08-17 14:10:03 +02:00
|
|
|
log.Debugf("loading pkce authorization flow info failed with error: %v", err)
|
|
|
|
log.Debugf("falling back to device authorization flow info")
|
2023-07-27 11:31:07 +02:00
|
|
|
|
2023-08-17 14:10:03 +02:00
|
|
|
deviceFlowInfo, err := internal.GetDeviceAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL)
|
2023-07-27 11:31:07 +02:00
|
|
|
if err != nil {
|
|
|
|
s, ok := gstatus.FromError(err)
|
|
|
|
if ok && s.Code() == codes.NotFound {
|
|
|
|
return nil, fmt.Errorf("no SSO provider returned from management. " +
|
|
|
|
"If you are using hosting Netbird see documentation at " +
|
|
|
|
"https://github.com/netbirdio/netbird/tree/main/management for details")
|
|
|
|
} else if ok && s.Code() == codes.Unimplemented {
|
|
|
|
return nil, fmt.Errorf("the management server, %s, does not support SSO providers, "+
|
|
|
|
"please update your server or use Setup Keys to login", config.ManagementURL)
|
|
|
|
} else {
|
2023-08-17 14:10:03 +02:00
|
|
|
return nil, fmt.Errorf("getting device authorization flow info failed with error: %v", err)
|
2023-07-27 11:31:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 14:10:03 +02:00
|
|
|
return NewDeviceAuthorizationFlow(deviceFlowInfo.ProviderConfig)
|
2023-07-27 11:31:07 +02:00
|
|
|
}
|