mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-07 23:52:05 +00:00
187 lines
4.5 KiB
Go
187 lines
4.5 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"context"
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"log"
|
||
|
|
"log/slog"
|
||
|
|
"net/http"
|
||
|
|
"os"
|
||
|
|
"os/signal"
|
||
|
|
"strings"
|
||
|
|
"syscall"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/zitadel/oidc/v3/example/server/exampleop"
|
||
|
|
"github.com/zitadel/oidc/v3/example/server/storage"
|
||
|
|
)
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
apiURL := os.Getenv("API_URL")
|
||
|
|
pat := readPAT(os.Getenv("PAT_FILE"))
|
||
|
|
domain := os.Getenv("API_DOMAIN")
|
||
|
|
schema := os.Getenv("SCHEMA")
|
||
|
|
host := os.Getenv("HOST")
|
||
|
|
port := os.Getenv("PORT")
|
||
|
|
|
||
|
|
logger := slog.New(
|
||
|
|
slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||
|
|
AddSource: true,
|
||
|
|
Level: slog.LevelDebug,
|
||
|
|
}),
|
||
|
|
)
|
||
|
|
|
||
|
|
issuer := fmt.Sprintf("%s://%s:%s/", schema, host, port)
|
||
|
|
redirectURI := fmt.Sprintf("%s/idps/callback", apiURL)
|
||
|
|
|
||
|
|
clientID := "web"
|
||
|
|
clientSecret := "secret"
|
||
|
|
storage.RegisterClients(
|
||
|
|
storage.WebClient(clientID, clientSecret, redirectURI),
|
||
|
|
)
|
||
|
|
|
||
|
|
storage := storage.NewStorage(storage.NewUserStore(issuer))
|
||
|
|
router := exampleop.SetupServer(issuer, storage, logger, false)
|
||
|
|
|
||
|
|
server := &http.Server{
|
||
|
|
Addr: ":" + port,
|
||
|
|
Handler: router,
|
||
|
|
}
|
||
|
|
go func() {
|
||
|
|
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||
|
|
log.Fatalf("HTTP server error: %v", err)
|
||
|
|
}
|
||
|
|
log.Println("Stopped serving new connections.")
|
||
|
|
}()
|
||
|
|
|
||
|
|
createZitadelResources(apiURL, pat, domain, issuer, clientID, clientSecret)
|
||
|
|
|
||
|
|
sigChan := make(chan os.Signal, 1)
|
||
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||
|
|
<-sigChan
|
||
|
|
|
||
|
|
shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
|
defer shutdownRelease()
|
||
|
|
|
||
|
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||
|
|
log.Fatalf("HTTP shutdown error: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func readPAT(path string) string {
|
||
|
|
f, err := os.Open(path)
|
||
|
|
if err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
pat, err := io.ReadAll(f)
|
||
|
|
if err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
return strings.Trim(string(pat), "\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func createZitadelResources(apiURL, pat, domain, issuer, clientID, clientSecret string) error {
|
||
|
|
idpID, err := CreateIDP(apiURL, pat, domain, issuer, clientID, clientSecret)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return ActivateIDP(apiURL, pat, domain, idpID)
|
||
|
|
}
|
||
|
|
|
||
|
|
type createIDP struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Issuer string `json:"issuer"`
|
||
|
|
ClientId string `json:"clientId"`
|
||
|
|
ClientSecret string `json:"clientSecret"`
|
||
|
|
Scopes []string `json:"scopes"`
|
||
|
|
ProviderOptions providerOptions `json:"providerOptions"`
|
||
|
|
IsIdTokenMapping bool `json:"isIdTokenMapping"`
|
||
|
|
UsePkce bool `json:"usePkce"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type providerOptions struct {
|
||
|
|
IsLinkingAllowed bool `json:"isLinkingAllowed"`
|
||
|
|
IsCreationAllowed bool `json:"isCreationAllowed"`
|
||
|
|
IsAutoCreation bool `json:"isAutoCreation"`
|
||
|
|
IsAutoUpdate bool `json:"isAutoUpdate"`
|
||
|
|
AutoLinking string `json:"autoLinking"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type idp struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func CreateIDP(apiURL, pat, domain string, issuer, clientID, clientSecret string) (string, error) {
|
||
|
|
createIDP := &createIDP{
|
||
|
|
Name: "OIDC",
|
||
|
|
Issuer: issuer,
|
||
|
|
ClientId: clientID,
|
||
|
|
ClientSecret: clientSecret,
|
||
|
|
Scopes: []string{"openid", "profile", "email"},
|
||
|
|
ProviderOptions: providerOptions{
|
||
|
|
IsLinkingAllowed: true,
|
||
|
|
IsCreationAllowed: true,
|
||
|
|
IsAutoCreation: true,
|
||
|
|
IsAutoUpdate: true,
|
||
|
|
AutoLinking: "AUTO_LINKING_OPTION_USERNAME",
|
||
|
|
},
|
||
|
|
IsIdTokenMapping: false,
|
||
|
|
UsePkce: false,
|
||
|
|
}
|
||
|
|
|
||
|
|
resp, err := doRequestWithHeaders(apiURL+"/admin/v1/idps/generic_oidc", pat, domain, createIDP)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
data, err := io.ReadAll(resp.Body)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
|
||
|
|
idp := new(idp)
|
||
|
|
if err := json.Unmarshal(data, idp); err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
return idp.ID, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type activateIDP struct {
|
||
|
|
IdpId string `json:"idpId"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func ActivateIDP(apiURL, pat, domain string, idpID string) error {
|
||
|
|
activateIDP := &activateIDP{
|
||
|
|
IdpId: idpID,
|
||
|
|
}
|
||
|
|
_, err := doRequestWithHeaders(apiURL+"/admin/v1/policies/login/idps", pat, domain, activateIDP)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func doRequestWithHeaders(apiURL, pat, domain string, body any) (*http.Response, error) {
|
||
|
|
data, err := json.Marshal(body)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
req, err := http.NewRequest(http.MethodPost, apiURL, io.NopCloser(bytes.NewReader(data)))
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
values := http.Header{}
|
||
|
|
values.Add("Authorization", "Bearer "+pat)
|
||
|
|
values.Add("x-forwarded-host", domain)
|
||
|
|
values.Add("Content-Type", "application/json")
|
||
|
|
req.Header = values
|
||
|
|
|
||
|
|
resp, err := http.DefaultClient.Do(req)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return resp, nil
|
||
|
|
}
|