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
}