-
Notifications
You must be signed in to change notification settings - Fork 3
/
logout.go
101 lines (82 loc) · 2.85 KB
/
logout.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package auth
import (
"context"
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"os"
"path"
"strings"
"github.com/int128/kubelogin/pkg/tokencache"
"github.com/int128/kubelogin/pkg/tokencache/repository"
"github.com/ninech/nctl/api"
"github.com/ninech/nctl/internal/format"
"k8s.io/client-go/util/homedir"
)
type LogoutCmd struct {
APIURL string `help:"The URL of the Nine API" default:"https://nineapis.ch" env:"NCTL_API_URL" name:"api-url"`
IssuerURL string `help:"Issuer URL is the OIDC issuer URL of the API." default:"https://auth.nine.ch/auth/realms/pub"`
ClientID string `help:"Client ID is the OIDC client ID of the API." default:"nineapis.ch-f178254"`
}
func (l *LogoutCmd) Run(ctx context.Context, command string, tk api.TokenGetter) error {
key := tokencache.Key{
ClientID: l.ClientID,
IssuerURL: l.IssuerURL,
}
filename, err := computeFilename(key)
if err != nil {
return err
}
filePath := path.Join(homedir.HomeDir(), api.DefaultTokenCachePath, filename)
if _, err = os.Stat(filePath); err != nil {
format.PrintFailuref("🤔", "seems like you are already logged out from %s", l.APIURL)
return nil
}
r := repository.Repository{}
cache, err := r.FindByKey(path.Join(homedir.HomeDir(), api.DefaultTokenCachePath), key)
if err != nil {
return fmt.Errorf("error finding cache file: %w", err)
}
form := url.Values{}
form.Add("client_id", l.ClientID)
form.Add("refresh_token", cache.RefreshToken)
logoutEndpoint := strings.Join([]string{l.IssuerURL, "protocol", "openid-connect", "logout"}, "/")
req, err := http.NewRequestWithContext(ctx, http.MethodPost, logoutEndpoint, strings.NewReader(form.Encode()))
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
token, err := tk.GetTokenString(ctx, l.IssuerURL, l.ClientID, false)
if err != nil {
return fmt.Errorf("error getting token: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Authorization", "Bearer "+token)
c := http.DefaultClient
resp, err := c.Do(req)
if err != nil {
return fmt.Errorf("error loging out from OIDC: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode <= 200 && resp.StatusCode > 300 {
return fmt.Errorf("http request error %d to %s", resp.StatusCode, logoutEndpoint)
}
if err := os.Remove(filePath); err != nil {
return fmt.Errorf("error removing the local cache: %w", err)
}
format.PrintSuccessf("👋", "logged out from %s", l.APIURL)
return nil
}
// This function is copied from kubelogin to find out the cache filename
// See: github.com/int128/kubelogin/pkg/tokencache/repository
func computeFilename(key tokencache.Key) (string, error) {
s := sha256.New()
e := gob.NewEncoder(s)
if err := e.Encode(&key); err != nil {
return "", fmt.Errorf("could not encode the key: %w", err)
}
h := hex.EncodeToString(s.Sum(nil))
return h, nil
}