feat(auth): GrantInfo — expose pending grant scopes + remaining TTL

The approve UI needs to show what a user code asks for and how long it
is valid; GrantInfo returns (scopes, secondsLeft, ok) without consuming
the grant (consumption stays one-shot in Token).
This commit is contained in:
mats committed 2026-08-16 14:29:22 +02:00
1 parent 2f3ea99745
commit 0423723d43
2 files changed
+47

No files matched your search

+26
View File
@@ -195,6 +195,32 @@ func TestDeviceFlowRequestedScopes(t *testing.T) {
}
}
func TestDeviceFlowGrantInfo(t *testing.T) {
now := time.Now()
f := NewDeviceFlow(NewLocalJWT("s"), DeviceFlowOptions{TTL: 10 * time.Minute})
f.now = func() time.Time { return now }
_, uc := mustStartScoped(t, f, "mcp", "read")
scopes, expiresIn, ok := f.GrantInfo(uc)
if !ok {
t.Fatal("pending grant should report info")
}
if len(scopes) != 2 || scopes[0] != "mcp" || scopes[1] != "read" {
t.Fatalf("scopes = %v, want [mcp read]", scopes)
}
if expiresIn <= 0 || expiresIn > 600 {
t.Fatalf("expiresIn = %d, want 0 < x <= 600", expiresIn)
}
// Unknown code.
if _, _, ok := f.GrantInfo("NOPE-XXXX"); ok {
t.Fatal("unknown code should report not found")
}
// Expired code reports not found (clock advanced past TTL).
f.now = func() time.Time { return now.Add(11 * time.Minute) }
if _, _, ok := f.GrantInfo(uc); ok {
t.Fatal("expired code should report not found")
}
}
func TestDeviceFlowFullLegacy(t *testing.T) {
f := NewDeviceFlow(NewLocalJWT("s"), DeviceFlowOptions{BaseURL: "https://admin.example.com"})
dc, uc, uri, complete, exp, interval := mustStart(t, f)