Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,9 @@ func refreshAccessToken(
data.Set("grant_type", "refresh_token")
data.Set("refresh_token", refreshToken)
data.Set("client_id", cfg.ClientID)
if !cfg.IsPublicClient() {
data.Set("client_secret", cfg.ClientSecret)
}
cfg.setClientSecret(data)
// Server doesn't persist extra_claims across refresh, so re-send them.
if cfg.ExtraClaims != "" {
data.Set(extraClaimsFormKey, cfg.ExtraClaims)
}
cfg.setExtraClaims(data)

tokenResp, err := doTokenExchange(ctx, cfg, cfg.Endpoints.TokenURL, data,
func(errResp ErrorResponse, _ []byte) error {
Expand Down
8 changes: 2 additions & 6 deletions browser_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,8 @@ func exchangeCode(
data.Set("client_id", cfg.ClientID)
data.Set("code_verifier", codeVerifier)

if !cfg.IsPublicClient() {
data.Set("client_secret", cfg.ClientSecret)
}
if cfg.ExtraClaims != "" {
data.Set(extraClaimsFormKey, cfg.ExtraClaims)
}
cfg.setClientSecret(data)
cfg.setExtraClaims(data)

tokenResp, err := doTokenExchange(ctx, cfg, cfg.Endpoints.TokenURL, data, nil)
if err != nil {
Expand Down
8 changes: 3 additions & 5 deletions device_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func requestDeviceCode(ctx context.Context, cfg *AppConfig) (*oauth2.DeviceAuthR
UserCode: deviceResp.UserCode,
VerificationURI: deviceResp.VerificationURI,
VerificationURIComplete: deviceResp.VerificationURIComplete,
Expiry: time.Now().Add(time.Duration(deviceResp.ExpiresIn) * time.Second),
Expiry: expiryFromNow(deviceResp.ExpiresIn),
Interval: int64(deviceResp.Interval),
}, nil
}
Expand Down Expand Up @@ -151,9 +151,7 @@ func exchangeDeviceCode(
data.Set("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
data.Set("device_code", deviceCode)
data.Set("client_id", cID)
if cfg.ExtraClaims != "" {
data.Set(extraClaimsFormKey, cfg.ExtraClaims)
}
cfg.setExtraClaims(data)

resp, err := cfg.RetryClient.Post(reqCtx, tokenURL,
retry.WithBody("application/x-www-form-urlencoded", strings.NewReader(data.Encode())),
Expand Down Expand Up @@ -192,7 +190,7 @@ func exchangeDeviceCode(
AccessToken: tokenResp.AccessToken,
RefreshToken: tokenResp.RefreshToken,
TokenType: tokenResp.TokenType,
Expiry: time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second),
Expiry: expiryFromNow(tokenResp.ExpiresIn),
}, nil
}

Expand Down
4 changes: 1 addition & 3 deletions token_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,7 @@ func doRevoke(
if tokenTypeHint != "" {
data.Set("token_type_hint", tokenTypeHint)
}
if !cfg.IsPublicClient() {
data.Set("client_secret", cfg.ClientSecret)
}
cfg.setClientSecret(data)
resp, err := cfg.RetryClient.Post(ctx, revokeURL,
retry.WithBody(
"application/x-www-form-urlencoded",
Expand Down
23 changes: 22 additions & 1 deletion tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,34 @@ func doTokenExchange(
return &tokenResp, nil
}

// expiryFromNow returns the absolute expiry time for a token that the server
// says expires in expiresIn seconds from now.
func expiryFromNow(expiresIn int) time.Time {
return time.Now().Add(time.Duration(expiresIn) * time.Second)
}

// setClientSecret adds the client_secret form parameter for confidential
// clients. Public (PKCE) clients omit it.
func (c *AppConfig) setClientSecret(data url.Values) {
if !c.IsPublicClient() {
data.Set("client_secret", c.ClientSecret)
}
}

// setExtraClaims adds the extra_claims form parameter when configured.
func (c *AppConfig) setExtraClaims(data url.Values) {
if c.ExtraClaims != "" {
data.Set(extraClaimsFormKey, c.ExtraClaims)
}
}

// tokenResponseToCredstore converts a tokenResponse to a credstore.Token.
func tokenResponseToCredstore(cfg *AppConfig, tr *tokenResponse) *credstore.Token {
return &credstore.Token{
AccessToken: tr.AccessToken,
RefreshToken: tr.RefreshToken,
TokenType: tr.TokenType,
ExpiresAt: time.Now().Add(time.Duration(tr.ExpiresIn) * time.Second),
ExpiresAt: expiryFromNow(tr.ExpiresIn),
ClientID: cfg.ClientID,
}
}
Expand Down
17 changes: 8 additions & 9 deletions tui/flow_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ type FlowRenderer struct {
spinnerFrame int
spinnerChars []rune
contentDirty bool // Flag to indicate if content needs redraw
inProgressStepIdx int // Index of the in-progress step for spinner updates
hasInProgressStep bool // Whether there's a step in progress
inProgressStepIdx int // Index of the in-progress step for spinner updates, or -1 if none
deviceUserCode string // Device code to display
deviceVerificationURI string // Device verification URL
deviceVerificationURIComplete string // Complete URL with user code
Expand All @@ -41,9 +40,10 @@ func NewFlowRenderer(flowType, clientMode, serverURL string) *FlowRenderer {
model.height = height

return &FlowRenderer{
model: model,
spinnerFrame: 0,
spinnerChars: []rune{'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'},
model: model,
spinnerFrame: 0,
spinnerChars: []rune{'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'},
inProgressStepIdx: -1,
}
}

Expand All @@ -65,10 +65,9 @@ func (r *FlowRenderer) UpdateStep(index int, status FlowStepStatus, message stri

// Track if this step is in progress for spinner animation
if status == StepInProgress {
r.hasInProgressStep = true
r.inProgressStepIdx = index
} else if r.inProgressStepIdx == index {
r.hasInProgressStep = false
r.inProgressStepIdx = -1
}
}

Expand Down Expand Up @@ -146,7 +145,7 @@ func (r *FlowRenderer) UpdateDisplay() {
resized := r.checkResize()

// If only spinner changed (not content), do a minimal update
if !r.contentDirty && r.hasInProgressStep {
if !r.contentDirty && r.inProgressStepIdx >= 0 {
r.updateSpinnerOnly()
return
}
Expand Down Expand Up @@ -198,7 +197,7 @@ func (r *FlowRenderer) UpdateDisplay() {

// updateSpinnerOnly updates only the spinner character without redrawing everything
func (r *FlowRenderer) updateSpinnerOnly() {
if !r.hasInProgressStep {
if r.inProgressStepIdx < 0 {
return
}

Expand Down
Loading