@@ -16,6 +16,7 @@ import (
1616 "net/url"
1717 "strings"
1818 "sync"
19+ "time"
1920
2021 "golang.org/x/oauth2/internal"
2122)
@@ -74,8 +75,9 @@ type TokenSource interface {
7475// Endpoint contains the OAuth 2.0 provider's authorization and token
7576// endpoint URLs.
7677type Endpoint struct {
77- AuthURL string
78- TokenURL string
78+ AuthURL string
79+ DeviceAuthURL string
80+ TokenURL string
7981}
8082
8183var (
@@ -203,6 +205,63 @@ func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOpti
203205 return retrieveToken (ctx , c , v )
204206}
205207
208+ // AuthDevice returns a device auth struct which contains a device code
209+ // and authorization information provided for users to enter on another device.
210+ func (c * Config ) AuthDevice (ctx context.Context , opts ... AuthCodeOption ) (* DeviceAuth , error ) {
211+ v := url.Values {
212+ "client_id" : {c .ClientID },
213+ }
214+ if len (c .Scopes ) > 0 {
215+ v .Set ("scope" , strings .Join (c .Scopes , " " ))
216+ }
217+ for _ , opt := range opts {
218+ opt .setValue (v )
219+ }
220+ return retrieveDeviceAuth (ctx , c , v )
221+ }
222+
223+ // Poll does a polling to exchange an device code for a token.
224+ func (c * Config ) Poll (ctx context.Context , da * DeviceAuth , opts ... AuthCodeOption ) (* Token , error ) {
225+ v := url.Values {
226+ "client_id" : {c .ClientID },
227+ "grant_type" : {"urn:ietf:params:oauth:grant-type:device_code" },
228+ "device_code" : {da .DeviceCode },
229+ "code" : {da .DeviceCode },
230+ }
231+ if len (c .Scopes ) > 0 {
232+ v .Set ("scope" , strings .Join (c .Scopes , " " ))
233+ }
234+ for _ , opt := range opts {
235+ opt .setValue (v )
236+ }
237+
238+ // If no interval was provided, the client MUST use a reasonable default polling interval.
239+ // See https://tools.ietf.org/html/draft-ietf-oauth-device-flow-07#section-3.5
240+ interval := da .Interval
241+ if interval == 0 {
242+ interval = 5
243+ }
244+
245+ for {
246+ time .Sleep (time .Duration (interval ) * time .Second )
247+
248+ tok , err := retrieveToken (ctx , c , v )
249+ if err == nil {
250+ return tok , nil
251+ }
252+
253+ errTyp := parseError (err )
254+ switch errTyp {
255+ case errAccessDenied , errExpiredToken :
256+ return tok , errors .New ("oauth2: " + errTyp )
257+ case errSlowDown :
258+ interval += 5
259+ fallthrough
260+ case errAuthorizationPending :
261+ }
262+ }
263+ }
264+
206265// Client returns an HTTP client using the provided token.
207266// The token will auto-refresh as necessary. The underlying
208267// HTTP transport will be obtained using the provided context.
0 commit comments