feat: add AuthProvider model and identity provider presets
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
|
||||
)
|
||||
|
||||
// Preset describes one well-known identity provider.
|
||||
//
|
||||
// This is a Go table rather than a collection on purpose: adding a preset is a
|
||||
// commit and a review, not a row somebody typed into production.
|
||||
type Preset struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"` // shown in the add-provider picker
|
||||
Kind string `json:"kind"` // models.KindOIDC | models.KindOAuth2
|
||||
IssuerFormat string `json:"-"` // %s is replaced by InputValue; empty means no issuer
|
||||
InputLabel string `json:"input_label"` // empty means the preset asks for nothing
|
||||
InputHint string `json:"input_hint"`
|
||||
Scopes []string `json:"-"`
|
||||
}
|
||||
|
||||
var presets = []Preset{
|
||||
{
|
||||
ID: "entra",
|
||||
Label: "Microsoft Entra ID",
|
||||
Kind: models.KindOIDC,
|
||||
IssuerFormat: "https://login.microsoftonline.com/%s/v2.0",
|
||||
InputLabel: "Directory (tenant) ID",
|
||||
InputHint: "Found in Entra under Overview. A UUID, not your domain name.",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
},
|
||||
{
|
||||
ID: "google",
|
||||
Label: "Google Workspace",
|
||||
Kind: models.KindOIDC,
|
||||
IssuerFormat: "https://accounts.google.com",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
},
|
||||
{
|
||||
ID: "okta",
|
||||
Label: "Okta",
|
||||
Kind: models.KindOIDC,
|
||||
IssuerFormat: "https://%s/oauth2/default",
|
||||
InputLabel: "Okta org domain",
|
||||
InputHint: "e.g. acme.okta.com — no scheme, no trailing slash.",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
},
|
||||
{
|
||||
ID: "github",
|
||||
Label: "GitHub",
|
||||
Kind: models.KindOAuth2,
|
||||
Scopes: []string{"read:user", "user:email"},
|
||||
},
|
||||
{
|
||||
ID: "",
|
||||
Label: "Other (OpenID Connect)",
|
||||
Kind: models.KindOIDC,
|
||||
IssuerFormat: "%s",
|
||||
InputLabel: "Issuer URL",
|
||||
InputHint: "The discovery base, e.g. https://id.example.com/realms/main",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
},
|
||||
}
|
||||
|
||||
// Presets returns the table for the settings UI to render a picker from.
|
||||
func Presets() []Preset {
|
||||
out := make([]Preset, len(presets))
|
||||
copy(out, presets)
|
||||
return out
|
||||
}
|
||||
|
||||
func PresetByID(id string) (Preset, bool) {
|
||||
for _, p := range presets {
|
||||
if p.ID == id {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return Preset{}, false
|
||||
}
|
||||
|
||||
// ExpandIssuer turns what the customer typed into the issuer URL that gets
|
||||
// stored. The stored value is always fully resolved, so nothing downstream has
|
||||
// to know a preset was involved.
|
||||
func ExpandIssuer(presetID, input string) (string, error) {
|
||||
p, ok := PresetByID(presetID)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unknown provider preset %q", presetID)
|
||||
}
|
||||
if p.IssuerFormat == "" {
|
||||
return "", nil // OAuth2 providers have no issuer
|
||||
}
|
||||
if !strings.Contains(p.IssuerFormat, "%s") {
|
||||
return p.IssuerFormat, nil // fixed issuer, input ignored
|
||||
}
|
||||
input = strings.TrimSpace(strings.TrimSuffix(input, "/"))
|
||||
if input == "" {
|
||||
return "", fmt.Errorf("%s is required", p.InputLabel)
|
||||
}
|
||||
return fmt.Sprintf(p.IssuerFormat, input), nil
|
||||
}
|
||||
|
||||
func DefaultScopes(presetID string) []string {
|
||||
p, ok := PresetByID(presetID)
|
||||
if !ok {
|
||||
return []string{"openid", "profile", "email"}
|
||||
}
|
||||
out := make([]string, len(p.Scopes))
|
||||
copy(out, p.Scopes)
|
||||
return out
|
||||
}
|
||||
|
||||
func KindFor(presetID string) string {
|
||||
if p, ok := PresetByID(presetID); ok {
|
||||
return p.Kind
|
||||
}
|
||||
return models.KindOIDC
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
// AuthProvider is one configured identity provider for one instance.
|
||||
//
|
||||
// ProviderID is a short random identifier rather than the Mongo _id: it appears
|
||||
// in the callback URL a customer pastes into their identity provider, and an
|
||||
// _id there would publish a database key.
|
||||
type AuthProvider struct {
|
||||
ID bson.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
|
||||
InstanceID string `bson:"instance_id" json:"instance_id"`
|
||||
ProviderID string `bson:"provider_id" json:"provider_id"`
|
||||
Name string `bson:"name" json:"name"`
|
||||
Kind string `bson:"kind" json:"kind"` // "oidc" | "oauth2"
|
||||
Preset string `bson:"preset" json:"preset"` // "" for custom
|
||||
Issuer string `bson:"issuer" json:"issuer"`
|
||||
ClientID string `bson:"client_id" json:"client_id"`
|
||||
ClientSecretEnc string `bson:"client_secret_enc,omitempty" json:"-"`
|
||||
Scopes []string `bson:"scopes" json:"scopes"`
|
||||
Enabled bool `bson:"enabled" json:"enabled"`
|
||||
// CallbackNotice marks a provider whose redirect URI changed at the upgrade
|
||||
// to per-provider callbacks. Set only by migration 0005; cleared when an
|
||||
// administrator acknowledges it in settings.
|
||||
CallbackNotice bool `bson:"callback_notice" json:"callback_notice"`
|
||||
Order int `bson:"order" json:"order"`
|
||||
CreatedAt time.Time `bson:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
const (
|
||||
KindOIDC = "oidc"
|
||||
KindOAuth2 = "oauth2"
|
||||
)
|
||||
Reference in New Issue
Block a user