1package idp
2
3import (
4 "context"
5 "crypto/hkdf"
6 "crypto/sha256"
7 "encoding/base64"
8 "errors"
9 "fmt"
10
11 dexstorage "github.com/dexidp/dex/storage"
12)
13
14const (
15 ForwardClientID = "_forward"
16 PortalClientID = "_portal"
17 AdminClientID = "_admin"
18)
19
20func FirstPartySecret(masterKey []byte, clientID string) (string, error) {
21 if len(masterKey) != 32 {
22 return "", errors.New("idp: the master key is not 32 bytes")
23 }
24 secret, err := hkdf.Key(sha256.New, masterKey, nil, "tug client "+clientID+" v1", 32)
25 if err != nil {
26 return "", fmt.Errorf("idp: derive the %s secret: %w", clientID, err)
27 }
28 return base64.RawURLEncoding.EncodeToString(secret), nil
29}
30
31func staticClients(cfg Config) ([]dexstorage.Client, error) {
32 clients := make([]dexstorage.Client, 0, len(cfg.Clients)+3)
33 for _, c := range cfg.Clients {
34 client := dexstorage.Client{ID: c.ID, Name: c.ID, RedirectURIs: append([]string(nil), c.RedirectURIs...), Public: c.Public}
35 if !c.Public {
36 secret := cfg.ClientSecrets[c.ID].Bytes()
37 if len(secret) == 0 {
38 return nil, fmt.Errorf("idp: confidential client %q has no secret", c.ID)
39 }
40 client.Secret = string(secret)
41 }
42 clients = append(clients, client)
43 }
44 forward := make([]string, 0, len(cfg.Apps))
45 for _, app := range cfg.Apps {
46 origin, ok := cfg.AppOrigins[app.Domain]
47 if !ok {
48 origin = "https://" + app.Domain
49 }
50 forward = append(forward, origin+"/.sso/callback")
51 }
52 for _, fp := range []struct {
53 id string
54 redirect []string
55 }{
56 {ForwardClientID, forward},
57 {PortalClientID, []string{cfg.AccountOrigin + "/oidc/callback"}},
58 {AdminClientID, []string{cfg.AdminOrigin + "/oidc/callback"}},
59 } {
60 secret, err := FirstPartySecret(cfg.MasterKey.Bytes(), fp.id)
61 if err != nil {
62 return nil, err
63 }
64 clients = append(clients, dexstorage.Client{ID: fp.id, Name: fp.id, Secret: secret, RedirectURIs: fp.redirect})
65 }
66 return clients, nil
67}
68
69func deleteStoredClients(ctx context.Context, dex dexstorage.Storage) error {
70 stored, err := dex.ListClients(ctx)
71 if err != nil {
72 return fmt.Errorf("idp: list stored clients: %w", err)
73 }
74 for _, c := range stored {
75 if err := dex.DeleteClient(ctx, c.ID); err != nil {
76 return fmt.Errorf("idp: delete stored client %q: %w", c.ID, err)
77 }
78 }
79 return nil
80}