@@ -41,6 +41,7 @@ import (
41
41
"github.com/coder/coder/v2/coderd/promoauth"
42
42
"github.com/coder/coder/v2/coderd/util/syncmap"
43
43
"github.com/coder/coder/v2/codersdk"
44
+ "github.com/coder/coder/v2/testutil"
44
45
)
45
46
46
47
type token struct {
@@ -484,6 +485,30 @@ func (f *FakeIDP) ExternalLogin(t testing.TB, client *codersdk.Client, opts ...f
484
485
_ = res .Body .Close ()
485
486
}
486
487
488
+ // DeviceLogin does the oauth2 device flow for external auth providers.
489
+ func (f * FakeIDP )DeviceLogin (t testing.TB ,client * codersdk.Client ,externalAuthID string ) {
490
+ // First we need to initiate the device flow. This will have Coder hit the
491
+ // fake IDP and get a device code.
492
+ device ,err := client .ExternalAuthDeviceByID (context .Background (),externalAuthID )
493
+ require .NoError (t ,err )
494
+
495
+ // Now the user needs to go to the fake IDP page and click "allow" and enter
496
+ // the device code input. For our purposes, we just send an http request to
497
+ // the verification url. No additional user input is needed.
498
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitShort )
499
+ defer cancel ()
500
+ _ ,err = client .Request (ctx ,http .MethodPost ,device .VerificationURI ,nil )
501
+ require .NoError (t ,err )
502
+
503
+ // Now we need to exchange the device code for an access token. We do this
504
+ // in this method because it is the user that does the polling for the device
505
+ // auth flow, not the backend.
506
+ err = client .ExternalAuthDeviceExchange (context .Background (),externalAuthID , codersdk.ExternalAuthDeviceExchange {
507
+ DeviceCode :device .DeviceCode ,
508
+ })
509
+ require .NoError (t ,err )
510
+ }
511
+
487
512
// CreateAuthCode emulates a user clicking "allow" on the IDP page. When doing
488
513
// unit tests, it's easier to skip this step sometimes. It does make an actual
489
514
// request to the IDP, so it should be equivalent to doing this "manually" with