1
- use std:: { io :: Write , time:: Duration } ;
1
+ use std:: { borrow :: Cow , future :: Future , time:: Duration } ;
2
2
3
- use http:: StatusCode ;
3
+ use http:: { StatusCode , Uri } ;
4
4
use reqwest:: Client ;
5
5
use tracing:: debug;
6
6
7
- use crate :: {
8
- auth:: device_flow:: {
9
- DeviceAccessTokenErrorResponse , DeviceAccessTokenRequest , DeviceAccessTokenResponse ,
10
- DeviceAuthorizationRequest , DeviceAuthorizationResponse ,
11
- } ,
12
- config,
7
+ use crate :: device_flow:: {
8
+ DeviceAccessTokenErrorResponse , DeviceAccessTokenRequest , DeviceAccessTokenResponse ,
9
+ DeviceAuthorizationRequest , DeviceAuthorizationResponse ,
13
10
} ;
14
11
12
+ const USER_AGENT : & str = concat ! ( env!( "CARGO_PKG_NAME" ) , "/" , env!( "CARGO_PKG_VERSION" ) ) ;
13
+
15
14
/// https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow
16
15
#[ derive( Clone ) ]
17
16
pub struct DeviceFlow {
18
17
client : Client ,
19
- client_id : & ' static str ,
18
+ client_id : Cow < ' static , str > ,
20
19
endpoint : Option < & ' static str > ,
21
20
}
22
21
23
22
impl DeviceFlow {
24
23
const DEVICE_AUTHORIZATION_ENDPOINT : & ' static str = "https://github.com/login/device/code" ;
25
24
const TOKEN_ENDPOINT : & ' static str = "https://github.com/login/oauth/access_token" ;
26
25
27
- pub fn new ( ) -> Self {
26
+ pub fn new ( client_id : impl Into < Cow < ' static , str > > ) -> Self {
28
27
let client = reqwest:: ClientBuilder :: new ( )
29
- . user_agent ( config :: USER_AGENT )
28
+ . user_agent ( USER_AGENT )
30
29
. timeout ( Duration :: from_secs ( 5 ) )
31
30
. build ( )
32
31
. unwrap ( ) ;
33
32
34
33
Self {
35
34
client,
36
- client_id : config :: github :: CLIENT_ID ,
35
+ client_id : client_id . into ( ) ,
37
36
endpoint : None ,
38
37
}
39
38
}
@@ -57,7 +56,7 @@ impl DeviceFlow {
57
56
. post ( self . endpoint . unwrap_or ( Self :: DEVICE_AUTHORIZATION_ENDPOINT ) )
58
57
. header ( http:: header:: ACCEPT , "application/json" )
59
58
. form ( & DeviceAuthorizationRequest {
60
- client_id : self . client_id . into ( ) ,
59
+ client_id : self . client_id . clone ( ) ,
61
60
scope : scope. into ( ) ,
62
61
} )
63
62
. send ( )
@@ -99,7 +98,10 @@ impl DeviceFlow {
99
98
. client
100
99
. post ( Self :: TOKEN_ENDPOINT )
101
100
. header ( http:: header:: ACCEPT , "application/json" )
102
- . form ( & DeviceAccessTokenRequest :: new ( & device_code, self . client_id ) )
101
+ . form ( & DeviceAccessTokenRequest :: new (
102
+ & device_code,
103
+ self . client_id . as_ref ( ) ,
104
+ ) )
103
105
. send ( )
104
106
. await ?;
105
107
@@ -128,10 +130,11 @@ impl DeviceFlow {
128
130
}
129
131
130
132
#[ tracing:: instrument( skip_all) ]
131
- pub async fn device_flow < W : Write > (
132
- self ,
133
- writer : W ,
134
- ) -> anyhow:: Result < DeviceAccessTokenResponse > {
133
+ pub async fn device_flow < F , Fut > ( self , callback : F ) -> anyhow:: Result < DeviceAccessTokenResponse >
134
+ where
135
+ F : FnOnce ( Uri , String ) -> Fut ,
136
+ Fut : Future < Output = ( ) > ,
137
+ {
135
138
let DeviceAuthorizationResponse {
136
139
device_code,
137
140
user_code,
@@ -140,12 +143,7 @@ impl DeviceFlow {
140
143
..
141
144
} = self . device_authorize_request ( ) . await ?;
142
145
143
- let mut writer = writer;
144
- writeln ! ( & mut writer, "Open `{verification_uri}` on your browser" ) ?;
145
- writeln ! ( & mut writer, "Enter CODE: `{user_code}`" ) ?;
146
-
147
- // attempt to open input screen in the browser
148
- open:: that ( verification_uri. to_string ( ) ) . ok ( ) ;
146
+ callback ( verification_uri, user_code) . await ;
149
147
150
148
self . pool_device_access_token ( device_code, interval) . await
151
149
}
0 commit comments