Raw
 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}