Skip to content

Commit 3d6a354

Browse files
authored
feat: add refreshing access token functionality to get-access-token command (#847)
1 parent 64f3f14 commit 3d6a354

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

internal/cmd/auth/get-access-token/get_access_token.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,12 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
2929
return &cliErr.SessionExpiredError{}
3030
}
3131

32-
accessToken, err := auth.GetAccessToken()
32+
// Try to get a valid access token, refreshing if necessary
33+
accessToken, err := auth.RefreshAccessToken(params.Printer)
3334
if err != nil {
3435
return err
3536
}
3637

37-
accessTokenExpired, err := auth.TokenExpired(accessToken)
38-
if err != nil {
39-
return err
40-
}
41-
if accessTokenExpired {
42-
return &cliErr.AccessTokenExpiredError{}
43-
}
44-
4538
params.Printer.Outputf("%s\n", accessToken)
4639
return nil
4740
},

internal/pkg/auth/auth.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package auth
22

33
import (
44
"fmt"
5+
"net/http"
56
"os"
67
"strconv"
78
"time"
@@ -132,3 +133,71 @@ func getEmailFromToken(token string) (string, error) {
132133

133134
return claims.Email, nil
134135
}
136+
137+
// RefreshAccessToken refreshes the access token if it's expired for the user token flow.
138+
// It returns the new access token or an error if the refresh fails.
139+
func RefreshAccessToken(p *print.Printer) (string, error) {
140+
flow, err := GetAuthFlow()
141+
if err != nil {
142+
return "", fmt.Errorf("get authentication flow: %w", err)
143+
}
144+
if flow != AUTH_FLOW_USER_TOKEN {
145+
return "", fmt.Errorf("token refresh is only supported for user token flow, current flow: %s", flow)
146+
}
147+
148+
// Load tokens from storage
149+
authFields := map[authFieldKey]string{
150+
ACCESS_TOKEN: "",
151+
REFRESH_TOKEN: "",
152+
IDP_TOKEN_ENDPOINT: "",
153+
}
154+
err = GetAuthFieldMap(authFields)
155+
if err != nil {
156+
return "", fmt.Errorf("get tokens from auth storage: %w", err)
157+
}
158+
159+
accessToken := authFields[ACCESS_TOKEN]
160+
refreshToken := authFields[REFRESH_TOKEN]
161+
tokenEndpoint := authFields[IDP_TOKEN_ENDPOINT]
162+
163+
if accessToken == "" {
164+
return "", fmt.Errorf("access token not set")
165+
}
166+
if refreshToken == "" {
167+
return "", fmt.Errorf("refresh token not set")
168+
}
169+
if tokenEndpoint == "" {
170+
return "", fmt.Errorf("token endpoint not set")
171+
}
172+
173+
// Check if access token is expired
174+
accessTokenExpired, err := TokenExpired(accessToken)
175+
if err != nil {
176+
return "", fmt.Errorf("check if access token has expired: %w", err)
177+
}
178+
if !accessTokenExpired {
179+
// Token is still valid, return it
180+
return accessToken, nil
181+
}
182+
183+
p.Debug(print.DebugLevel, "access token expired, refreshing...")
184+
185+
// Create a temporary userTokenFlow to reuse the refresh logic
186+
utf := &userTokenFlow{
187+
printer: p,
188+
client: &http.Client{},
189+
authFlow: flow,
190+
accessToken: accessToken,
191+
refreshToken: refreshToken,
192+
tokenEndpoint: tokenEndpoint,
193+
}
194+
195+
// Refresh the tokens
196+
err = refreshTokens(utf)
197+
if err != nil {
198+
return "", fmt.Errorf("refresh access token: %w", err)
199+
}
200+
201+
// Return the new access token
202+
return utf.accessToken, nil
203+
}

0 commit comments

Comments
 (0)