feat: add AuthProvider model and identity provider presets

This commit is contained in:
2026-08-03 10:30:21 +01:00
parent c56bfb7270
commit 45f7c0c393
2 changed files with 157 additions and 0 deletions
+119
View File
@@ -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
}
+38
View File
@@ -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"
)