Skip to content

Commit 7153f5e

Browse files
feat: support PAT based device auth and new public APIs ✨ πŸš€ (#140)
1 parent 454b32f commit 7153f5e

21 files changed

Lines changed: 335 additions & 242 deletions

File tree

β€Ž.deepsource.tomlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ name = "test-coverage"
1616
enabled = true
1717

1818
[[transformers]]
19-
name = "gofmt"
19+
name = "gofumpt"
2020
enabled = true

β€Ž.github/workflows/CI.ymlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Set up Go 1.x
1616
uses: actions/setup-go@v2
1717
with:
18-
go-version: ^1.16
18+
go-version: ^1.18
1919

2020
- name: Check out code into the Go module directory
2121
uses: actions/checkout@v2

β€ŽMakefileβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ test:
1212
echo "\n====TESTING CONFIG VALIDATOR PACKAGE====\n"
1313
go test -v ./configvalidator/... -count=1
1414
echo "\n====CALCULATING TEST COVERAGE FOR ENTIRE PACKAGE====\n"
15-
go test -v -coverpkg=./... -coverprofile=coverage.out -count=1 ./...
15+
go test -v -coverprofile=coverage.out -count=1 ./...
1616

1717
test_setup:
1818
mkdir -p ${CODE_PATH}

β€Žcommand/auth/login/login_flow.goβ€Ž

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package login
33
import (
44
"context"
55
"fmt"
6+
"os"
7+
"os/user"
68
"time"
79

810
"github.com/cli/browser"
@@ -33,9 +35,9 @@ func (opts *LoginOptions) startLoginFlow(cfg *config.CLIConfig) error {
3335
return err
3436
}
3537

36-
// Fetch the JWT using the device registration resonse
37-
var jwtData *auth.JWT
38-
jwtData, opts.AuthTimedOut, err = fetchJWT(ctx, deviceRegistrationResponse)
38+
// Fetch the PAT using the device registration resonse
39+
var tokenData *auth.PAT
40+
tokenData, opts.AuthTimedOut, err = fetchPAT(ctx, deviceRegistrationResponse)
3941
if err != nil {
4042
return err
4143
}
@@ -47,11 +49,9 @@ func (opts *LoginOptions) startLoginFlow(cfg *config.CLIConfig) error {
4749

4850
// Storing the useful data for future reference and usage
4951
// in a global config object (Cfg)
50-
cfg.User = jwtData.Payload.Email
51-
cfg.Token = jwtData.Token
52-
cfg.RefreshToken = jwtData.Refreshtoken
53-
cfg.RefreshTokenExpiresIn = time.Unix(jwtData.RefreshExpiresIn, 0)
54-
cfg.SetTokenExpiry(jwtData.Payload.Exp)
52+
cfg.User = tokenData.User.Email
53+
cfg.Token = tokenData.Token
54+
cfg.SetTokenExpiry(tokenData.Expiry)
5555

5656
// Having stored the data in the global Cfg object, write it into the config file present in the local filesystem
5757
err = cfg.WriteFile()
@@ -79,11 +79,30 @@ func registerDevice(ctx context.Context) (*auth.Device, error) {
7979
return res, nil
8080
}
8181

82-
func fetchJWT(ctx context.Context, deviceRegistrationData *auth.Device) (*auth.JWT, bool, error) {
83-
var jwtData *auth.JWT
82+
func fetchPAT(ctx context.Context, deviceRegistrationData *auth.Device) (*auth.PAT, bool, error) {
83+
var tokenData *auth.PAT
8484
var err error
85+
defaultUserName := "user"
86+
defaultHostName := "host"
87+
userName := ""
8588
authTimedOut := true
8689

90+
/* ======================================================================= */
91+
// The username and hostname to add in the description for the PAT request
92+
/* ======================================================================= */
93+
userData, err := user.Current()
94+
if err != nil {
95+
userName = defaultUserName
96+
} else {
97+
userName = userData.Username
98+
}
99+
100+
hostName, err := os.Hostname()
101+
if err != nil {
102+
hostName = defaultHostName
103+
}
104+
userDescription := fmt.Sprintf("CLI PAT for %s@%s", userName, hostName)
105+
87106
// Fetching DeepSource client in order to interact with SDK
88107
deepsource, err := deepsource.New(deepsource.ClientOpts{
89108
Token: config.Cfg.Token,
@@ -97,10 +116,10 @@ func fetchJWT(ctx context.Context, deviceRegistrationData *auth.Device) (*auth.J
97116
ticker := time.NewTicker(time.Duration(deviceRegistrationData.Interval) * time.Second)
98117
pollStartTime := time.Now()
99118

100-
// Polling for fetching JWT
119+
// Polling for fetching PAT
101120
func() {
102121
for range ticker.C {
103-
jwtData, err = deepsource.Login(ctx, deviceRegistrationData.Code)
122+
tokenData, err = deepsource.Login(ctx, deviceRegistrationData.Code, userDescription)
104123
if err == nil {
105124
authTimedOut = false
106125
return
@@ -113,5 +132,5 @@ func fetchJWT(ctx context.Context, deviceRegistrationData *auth.Device) (*auth.J
113132
}
114133
}()
115134

116-
return jwtData, authTimedOut, nil
135+
return tokenData, authTimedOut, nil
117136
}

β€Žcommand/auth/refresh/refresh.goβ€Ž

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"time"
87

98
"github.com/MakeNowJust/heredoc"
109
"github.com/deepsourcelabs/cli/config"
@@ -18,7 +17,6 @@ type RefreshOptions struct{}
1817

1918
// NewCmdRefresh handles the refreshing of authentication credentials
2019
func NewCmdRefresh() *cobra.Command {
21-
2220
doc := heredoc.Docf(`
2321
Refresh stored authentication credentials.
2422
@@ -52,14 +50,6 @@ func (opts *RefreshOptions) Run() error {
5250
return errors.New("You are not logged into DeepSource. Run \"deepsource auth login\" to authenticate.")
5351
}
5452

55-
// Check if token as well as refresh token are present since they
56-
// are required for refreshing auth
57-
if cfg.Token == "" || cfg.RefreshToken == "" {
58-
// In case, there is no token as well as refresh token, ask the user to login instead
59-
// TODO: Add ability to automatically run `login` command here
60-
return fmt.Errorf("You are not logged into DeepSource. Run \"deepsource auth login\" to authenticate.")
61-
}
62-
6353
// Fetching DS Client
6454
deepsource, err := deepsource.New(deepsource.ClientOpts{
6555
Token: config.Cfg.Token,
@@ -70,17 +60,15 @@ func (opts *RefreshOptions) Run() error {
7060
}
7161
ctx := context.Background()
7262
// Use the SDK to fetch the new auth data
73-
refreshedConfigData, err := deepsource.RefreshAuthCreds(ctx, cfg.RefreshToken)
63+
refreshedConfigData, err := deepsource.RefreshAuthCreds(ctx, cfg.Token)
7464
if err != nil {
7565
return err
7666
}
7767

7868
// Convert incoming config into the local CLI config format
79-
cfg.User = refreshedConfigData.Payload.Email
69+
cfg.User = refreshedConfigData.User.Email
8070
cfg.Token = refreshedConfigData.Token
81-
cfg.RefreshToken = refreshedConfigData.Refreshtoken
82-
cfg.RefreshTokenExpiresIn = time.Unix(refreshedConfigData.RefreshExpiresIn, 0)
83-
cfg.SetTokenExpiry(refreshedConfigData.Payload.Exp)
71+
cfg.SetTokenExpiry(refreshedConfigData.Expiry)
8472

8573
// Having formatted the data, write it to the config file
8674
err = cfg.WriteFile()

β€Žcommand/issues/list/list.goβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ func (opts *IssuesListOptions) exportJSON(filename string) (err error) {
235235
return nil
236236
}
237237

238-
if err = ioutil.WriteFile(filename, data, 0644); err != nil {
238+
if err = ioutil.WriteFile(filename, data, 0o644); err != nil {
239239
return err
240240
}
241241

β€Žconfig/config.goβ€Ž

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"github.com/pelletier/go-toml"
1010
)
1111

12-
var configDirFn = os.UserHomeDir
13-
var readFileFn = os.ReadFile
12+
var (
13+
configDirFn = os.UserHomeDir
14+
readFileFn = os.ReadFile
15+
)
1416

1517
const (
1618
ConfigDirName = "/.deepsource/"
@@ -19,22 +21,19 @@ const (
1921
)
2022

2123
type CLIConfig struct {
22-
Host string `toml:"host"`
23-
User string `toml:"user"`
24-
Token string `toml:"token"`
25-
RefreshToken string `toml:"refresh_token,omitempty"`
26-
TokenExpiresIn time.Time `toml:"token_expires_in,omitempty"`
27-
RefreshTokenExpiresIn time.Time `toml:"refresh_token_expires_in,omitempty"`
24+
Host string `toml:"host"`
25+
User string `toml:"user"`
26+
Token string `toml:"token"`
27+
TokenExpiresIn time.Time `toml:"token_expires_in,omitempty"`
2828
}
2929

3030
var Cfg CLIConfig
3131

32+
// Sets the token expiry in the desired format
3233
// Sets the token expiry in the desired format
3334
func (cfg *CLIConfig) SetTokenExpiry(str string) {
34-
layout := "2006-01-02T15:04:05.999999999"
35-
t, _ := time.Parse(layout, str)
36-
timeStamp := t.Unix()
37-
cfg.TokenExpiresIn = time.Unix(timeStamp, 0)
35+
t, _ := time.Parse(time.RFC3339, str)
36+
cfg.TokenExpiresIn = t.UTC()
3837
}
3938

4039
// Checks if the token has expired or not
@@ -63,7 +62,7 @@ func (cfg CLIConfig) configPath() (string, error) {
6362
return filepath.Join(home, ConfigFileName), nil
6463
}
6564

66-
//ReadFile reads the CLI config file.
65+
// ReadFile reads the CLI config file.
6766
func (cfg *CLIConfig) ReadConfigFile() error {
6867
path, err := cfg.configPath()
6968
if err != nil {
@@ -89,7 +88,6 @@ func (cfg *CLIConfig) ReadConfigFile() error {
8988
}
9089

9190
func GetConfig() (*CLIConfig, error) {
92-
9391
if Cfg.Token != "" {
9492
return &Cfg, nil
9593
}
@@ -103,7 +101,6 @@ func GetConfig() (*CLIConfig, error) {
103101

104102
// WriteFile writes the CLI config to file.
105103
func (cfg *CLIConfig) WriteFile() error {
106-
107104
data, err := toml.Marshal(cfg)
108105
if err != nil {
109106
return err
@@ -145,7 +142,6 @@ func (cfg *CLIConfig) Delete() error {
145142
}
146143

147144
func (cfg *CLIConfig) VerifyAuthentication() error {
148-
149145
// Checking if the user has authenticated / logged in or not
150146
if cfg.Token == "" {
151147
return errors.New("You are not logged into DeepSource. Run \"deepsource auth login\" to authenticate.")

β€Žconfig/config_test.goβ€Ž

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import (
88
)
99

1010
var cfg = CLIConfig{
11-
Host: "deepsource.io",
12-
User: "test",
13-
Token: "test_token",
14-
RefreshToken: "test_refresh_token",
15-
TokenExpiresIn: time.Time{},
16-
RefreshTokenExpiresIn: time.Time{},
11+
Host: "deepsource.io",
12+
User: "test",
13+
Token: "test_token",
14+
TokenExpiresIn: time.Time{},
1715
}
1816

1917
func TestSetTokenExpiry(t *testing.T) {
@@ -26,9 +24,9 @@ func TestSetTokenExpiry(t *testing.T) {
2624
})
2725

2826
t.Run("must work with valid timestamp", func(t *testing.T) {
29-
str := "2021-01-01T00:00:00.999999999"
27+
str := "9999-12-31T23:59:59.999999+00:00"
3028
cfg.SetTokenExpiry(str)
31-
want := "2021-01-01 00:00:00 +0000 UTC"
29+
want := "9999-12-31 23:59:59.999999 +0000 UTC"
3230

3331
assert.Equal(t, cfg.TokenExpiresIn.UTC().String(), want)
3432
})

β€Ždeepsource/auth/device.goβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ type Device struct {
66
VerificationURI string `json:"verificationUri"` // URL to verify user code
77
VerificationURIComplete string `json:"verificationUriComplete"` // URL to verify user code with the user code being sent as a URL param
88
ExpiresIn int `json:"expiresIn"` // Time in which the device code expires
9-
Interval int `json:"interval"` // Interval in which the client needs to poll at the endpoint to receive the JWT
9+
Interval int `json:"interval"` // Interval in which the client needs to poll at the endpoint to receive the PAT
1010
}

β€Ždeepsource/auth/jwt.goβ€Ž

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
Β (0)