@@ -73,6 +73,35 @@ func LoginWithToken(f *factory.Factory, org, token string) error {
7373 return nil
7474}
7575
76+ // LoginWithSession stores an OAuth session for an organization in the system keychain.
77+ func LoginWithSession (f * factory.Factory , org string , session * oauth.Session ) error {
78+ if org == "" {
79+ return errors .New ("organization cannot be empty" )
80+ }
81+ if session == nil || session .AccessToken == "" {
82+ return errors .New ("oauth session must include an access token" )
83+ }
84+
85+ kr := keyring .New ()
86+ if ! kr .IsAvailable () {
87+ return errors .New ("system keychain is not available; cannot store token" )
88+ }
89+ if err := kr .SetSession (org , session ); err != nil {
90+ return fmt .Errorf ("failed to store token in keychain: %w" , err )
91+ }
92+ fmt .Println ("Token stored securely in system keychain." )
93+
94+ if err := f .Config .EnsureOrganization (org ); err != nil {
95+ return fmt .Errorf ("failed to register organization in config: %w" , err )
96+ }
97+
98+ if err := f .Config .SelectOrganization (org , f .GitRepository != nil ); err != nil {
99+ return fmt .Errorf ("failed to select organization: %w" , err )
100+ }
101+
102+ return nil
103+ }
104+
76105func (c * LoginCmd ) Run (kongCtx * kong.Context , globals cli.GlobalFlags ) error {
77106 f , err := factory .New (factory .WithDebug (globals .EnableDebug ()))
78107 if err != nil {
@@ -95,8 +124,7 @@ func (c *LoginCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
95124 // Create OAuth flow
96125 cfg := & oauth.Config {
97126 // Host default handled via NewFlow, omitted to allow usage of BUILDKITE_HOST
98- ClientID : oauth .DefaultClientID ,
99- Scopes : c .Scopes ,
127+ Scopes : c .Scopes ,
100128 }
101129
102130 flow , err := oauth .NewFlow (cfg )
@@ -135,23 +163,13 @@ func (c *LoginCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
135163 return fmt .Errorf ("token exchange failed: %w" , err )
136164 }
137165
138- // Resolve org from the API using the new token
139- client , err := buildkite .NewOpts (buildkite .WithTokenAuth (tokenResp .AccessToken ))
140- if err != nil {
141- return fmt .Errorf ("failed to create API client: %w" , err )
142- }
143-
144- orgs , _ , err := client .Organizations .List (ctx , nil )
166+ org , err := resolveOrganizationFromToken (ctx , f .Config .RESTAPIEndpoint (), tokenResp .AccessToken )
145167 if err != nil {
146- return fmt .Errorf ("failed to list organizations: %w" , err )
147- }
148- if len (orgs ) == 0 {
149- return fmt .Errorf ("no organizations found for this token" )
168+ return err
150169 }
151170
152- org := orgs [0 ]
153-
154- if err := LoginWithToken (f , org .Slug , tokenResp .AccessToken ); err != nil {
171+ session := tokenResp .Session (cfg .Host , time .Now ())
172+ if err := LoginWithSession (f , org .Slug , session ); err != nil {
155173 return err
156174 }
157175
@@ -160,3 +178,23 @@ func (c *LoginCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
160178
161179 return nil
162180}
181+
182+ func resolveOrganizationFromToken (ctx context.Context , baseURL , token string ) (* buildkite.Organization , error ) {
183+ client , err := buildkite .NewOpts (
184+ buildkite .WithBaseURL (baseURL ),
185+ buildkite .WithTokenAuth (token ),
186+ )
187+ if err != nil {
188+ return nil , fmt .Errorf ("failed to create API client: %w" , err )
189+ }
190+
191+ orgs , _ , err := client .Organizations .List (ctx , nil )
192+ if err != nil {
193+ return nil , fmt .Errorf ("failed to list organizations: %w" , err )
194+ }
195+ if len (orgs ) == 0 {
196+ return nil , fmt .Errorf ("no organizations found for this token" )
197+ }
198+
199+ return & orgs [0 ], nil
200+ }
0 commit comments