mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 00:36:38 +00:00
add support for some basic authentication methods
This commit is contained in:
@@ -1,4 +1,14 @@
|
||||
<!doctype html>
|
||||
{{ range . }}
|
||||
<p>{{ . }}</p>
|
||||
{{ if eq .Key "pin" }}
|
||||
<form>
|
||||
<input name={{ . }} />
|
||||
<button type=submit></button>
|
||||
</form>
|
||||
{{ else if eq .Key "password" }}
|
||||
<form>
|
||||
<input name={{ . }} />
|
||||
<button type=submit></button>
|
||||
</form>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
57
proxy/internal/auth/link.go
Normal file
57
proxy/internal/auth/link.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
const linkFormId = "email"
|
||||
|
||||
type Link struct {
|
||||
id, accountId string
|
||||
client authenticator
|
||||
}
|
||||
|
||||
func NewLink(client authenticator, id, accountId string) Link {
|
||||
return Link{
|
||||
id: id,
|
||||
accountId: accountId,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (Link) Type() Method {
|
||||
return MethodLink
|
||||
}
|
||||
|
||||
func (l Link) Authenticate(r *http.Request) (string, bool, any) {
|
||||
email := r.FormValue(linkFormId)
|
||||
|
||||
res, err := l.client.Authenticate(r.Context(), &proto.AuthenticateRequest{
|
||||
Id: l.id,
|
||||
AccountId: l.accountId,
|
||||
Request: &proto.AuthenticateRequest_Link{
|
||||
Link: &proto.LinkRequest{
|
||||
Email: email,
|
||||
Redirect: "", // TODO: calculate this.
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
// TODO: log error here
|
||||
return "", false, linkFormId
|
||||
}
|
||||
|
||||
if res.GetSuccess() {
|
||||
// Use the email address as the user identifier.
|
||||
return email, true, nil
|
||||
}
|
||||
|
||||
return "", false, linkFormId
|
||||
}
|
||||
|
||||
func (l Link) Middleware(next http.Handler) http.Handler {
|
||||
// TODO: handle magic link redirects, should be similar to OIDC.
|
||||
return next
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
@@ -8,6 +9,10 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
//go:embed auth.gohtml
|
||||
@@ -16,9 +21,10 @@ var authTemplate string
|
||||
type Method string
|
||||
|
||||
var (
|
||||
MethodBasicAuth Method = "basic"
|
||||
MethodPIN Method = "pin"
|
||||
MethodBearer Method = "bearer"
|
||||
MethodPassword Method = "password"
|
||||
MethodPIN Method = "pin"
|
||||
MethodOIDC Method = "oidc"
|
||||
MethodLink Method = "link"
|
||||
)
|
||||
|
||||
func (m Method) String() string {
|
||||
@@ -36,6 +42,10 @@ type session struct {
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type authenticator interface {
|
||||
Authenticate(ctx context.Context, in *proto.AuthenticateRequest, opts ...grpc.CallOption) (*proto.AuthenticateResponse, error)
|
||||
}
|
||||
|
||||
type Scheme interface {
|
||||
Type() Method
|
||||
// Authenticate should check the passed request and determine whether
|
||||
|
||||
@@ -35,14 +35,15 @@ type oidcState struct {
|
||||
|
||||
// OIDC implements the Scheme interface for JWT/OIDC authentication
|
||||
type OIDC struct {
|
||||
verifier *oidc.IDTokenVerifier
|
||||
oauthConfig *oauth2.Config
|
||||
states map[string]*oidcState
|
||||
statesMux sync.RWMutex
|
||||
id, accountId string
|
||||
verifier *oidc.IDTokenVerifier
|
||||
oauthConfig *oauth2.Config
|
||||
states map[string]*oidcState
|
||||
statesMux sync.RWMutex
|
||||
}
|
||||
|
||||
// NewOIDC creates a new OIDC authentication scheme
|
||||
func NewOIDC(ctx context.Context, cfg OIDCConfig) (*OIDC, error) {
|
||||
func NewOIDC(ctx context.Context, id, accountId string, cfg OIDCConfig) (*OIDC, error) {
|
||||
if cfg.OIDCProviderURL == "" || cfg.OIDCClientID == "" {
|
||||
return nil, fmt.Errorf("OIDC provider URL and client ID are required")
|
||||
}
|
||||
@@ -58,6 +59,8 @@ func NewOIDC(ctx context.Context, cfg OIDCConfig) (*OIDC, error) {
|
||||
}
|
||||
|
||||
o := &OIDC{
|
||||
id: id,
|
||||
accountId: accountId,
|
||||
verifier: provider.Verifier(&oidc.Config{
|
||||
ClientID: cfg.OIDCClientID,
|
||||
}),
|
||||
@@ -77,7 +80,7 @@ func NewOIDC(ctx context.Context, cfg OIDCConfig) (*OIDC, error) {
|
||||
}
|
||||
|
||||
func (*OIDC) Type() Method {
|
||||
return MethodBearer
|
||||
return MethodOIDC
|
||||
}
|
||||
|
||||
func (o *OIDC) Authenticate(r *http.Request) (string, bool, any) {
|
||||
|
||||
62
proxy/internal/auth/password.go
Normal file
62
proxy/internal/auth/password.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
passwordUserId = "password-user"
|
||||
passwordFormId = "password"
|
||||
)
|
||||
|
||||
type Password struct {
|
||||
id, accountId string
|
||||
client authenticator
|
||||
}
|
||||
|
||||
func NewPassword(client authenticator, id, accountId string) Password {
|
||||
return Password{
|
||||
id: id,
|
||||
accountId: accountId,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (Password) Type() Method {
|
||||
return MethodPassword
|
||||
}
|
||||
|
||||
// Authenticate attempts to authenticate the request using a form
|
||||
// value passed in the request.
|
||||
// If authentication fails, the required HTTP form ID is returned
|
||||
// so that it can be injected into a request from the UI so that
|
||||
// authentication may be successful.
|
||||
func (p Password) Authenticate(r *http.Request) (string, bool, any) {
|
||||
password := r.FormValue(passwordFormId)
|
||||
|
||||
res, err := p.client.Authenticate(r.Context(), &proto.AuthenticateRequest{
|
||||
Id: p.id,
|
||||
AccountId: p.accountId,
|
||||
Request: &proto.AuthenticateRequest_Password{
|
||||
Password: &proto.PasswordRequest{
|
||||
Password: password,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
// TODO: log error here
|
||||
return "", false, passwordFormId
|
||||
}
|
||||
|
||||
if res.GetSuccess() {
|
||||
return passwordUserId, true, nil
|
||||
}
|
||||
|
||||
return "", false, passwordFormId
|
||||
}
|
||||
|
||||
func (p Password) Middleware(next http.Handler) http.Handler {
|
||||
return next
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"net/http"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
userId = "pin-user"
|
||||
formId = "pin"
|
||||
pinUserId = "pin-user"
|
||||
pinFormId = "pin"
|
||||
)
|
||||
|
||||
type Pin struct {
|
||||
pin string
|
||||
id, accountId string
|
||||
client authenticator
|
||||
}
|
||||
|
||||
func NewPin(pin string) Pin {
|
||||
func NewPin(client authenticator, id, accountId string) Pin {
|
||||
return Pin{
|
||||
pin: pin,
|
||||
id: id,
|
||||
accountId: accountId,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,14 +34,27 @@ func (Pin) Type() Method {
|
||||
// so that it can be injected into a request from the UI so that
|
||||
// authentication may be successful.
|
||||
func (p Pin) Authenticate(r *http.Request) (string, bool, any) {
|
||||
pin := r.FormValue(formId)
|
||||
pin := r.FormValue(pinFormId)
|
||||
|
||||
// Compare the passed pin with the expected pin.
|
||||
if subtle.ConstantTimeCompare([]byte(pin), []byte(p.pin)) == 1 {
|
||||
return userId, false, nil
|
||||
res, err := p.client.Authenticate(r.Context(), &proto.AuthenticateRequest{
|
||||
Id: p.id,
|
||||
AccountId: p.accountId,
|
||||
Request: &proto.AuthenticateRequest_Pin{
|
||||
Pin: &proto.PinRequest{
|
||||
Pin: pin,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
// TODO: log error here
|
||||
return "", false, pinFormId
|
||||
}
|
||||
|
||||
return "", false, formId
|
||||
if res.GetSuccess() {
|
||||
return pinUserId, true, nil
|
||||
}
|
||||
|
||||
return "", false, pinFormId
|
||||
}
|
||||
|
||||
func (p Pin) Middleware(next http.Handler) http.Handler {
|
||||
|
||||
Reference in New Issue
Block a user