@@ -17,10 +17,10 @@ use miette::{IntoDiagnostic, Result};
1717use oauth2:: basic:: BasicClient ;
1818use oauth2:: {
1919 AuthType , AuthUrl , AuthorizationCode , ClientId , ClientSecret , CsrfToken , PkceCodeChallenge ,
20- RedirectUrl , RefreshToken , Scope , TokenResponse , TokenUrl ,
20+ RedirectUrl , Scope , TokenResponse , TokenUrl ,
2121} ;
2222use openshell_bootstrap:: oidc_token:: OidcTokenBundle ;
23- use serde :: Deserialize ;
23+ use openshell_sdk :: oidc :: { RefreshTokenInput , discover , http_client , refresh_token } ;
2424use std:: convert:: Infallible ;
2525use std:: sync:: { Arc , Mutex } ;
2626use std:: time:: Duration ;
@@ -30,50 +30,6 @@ use tracing::debug;
3030
3131const AUTH_TIMEOUT : Duration = Duration :: from_secs ( 120 ) ;
3232
33- /// OIDC discovery document (subset of fields we need).
34- #[ derive( Debug , Deserialize ) ]
35- struct OidcDiscovery {
36- issuer : String ,
37- authorization_endpoint : String ,
38- token_endpoint : String ,
39- }
40-
41- /// Discover OIDC endpoints from the issuer's well-known configuration.
42- ///
43- /// Validates that the discovery document's `issuer` field matches the
44- /// configured issuer URL to prevent SSRF or misdirection.
45- async fn discover ( issuer : & str , insecure : bool ) -> Result < OidcDiscovery > {
46- let normalized_issuer = issuer. trim_end_matches ( '/' ) ;
47- let url = format ! ( "{normalized_issuer}/.well-known/openid-configuration" ) ;
48- let client = http_client ( insecure) ;
49- let resp: OidcDiscovery = client
50- . get ( & url)
51- . send ( )
52- . await
53- . into_diagnostic ( ) ?
54- . json ( )
55- . await
56- . into_diagnostic ( ) ?;
57-
58- let discovered_issuer = resp. issuer . trim_end_matches ( '/' ) ;
59- if discovered_issuer != normalized_issuer {
60- return Err ( miette:: miette!(
61- "OIDC discovery issuer mismatch: expected '{}', got '{}'" ,
62- normalized_issuer,
63- discovered_issuer
64- ) ) ;
65- }
66- Ok ( resp)
67- }
68-
69- fn http_client ( insecure : bool ) -> reqwest:: Client {
70- let mut builder = reqwest:: ClientBuilder :: new ( ) . redirect ( reqwest:: redirect:: Policy :: none ( ) ) ;
71- if insecure {
72- builder = builder. danger_accept_invalid_certs ( true ) ;
73- }
74- builder. build ( ) . expect ( "failed to build HTTP client" )
75- }
76-
7733fn build_scopes ( scopes : Option < & str > ) -> Vec < Scope > {
7834 let mut result = vec ! [ Scope :: new( "openid" . to_string( ) ) ] ;
7935 if let Some ( s) = scopes {
@@ -227,36 +183,33 @@ pub async fn oidc_client_credentials_flow(
227183
228184/// Refresh an OIDC token using the `refresh_token` grant.
229185///
230- /// Preserves the existing refresh token if the server does not return a new
231- /// one (per OAuth 2.0 spec, the refresh response may omit `refresh_token`).
186+ /// Wraps [`openshell_sdk::oidc::refresh_token`] with the CLI's
187+ /// [`OidcTokenBundle`] storage shape. Preserves the existing refresh
188+ /// token when the server omits one (per OAuth 2.0 the refresh response
189+ /// is allowed to leave `refresh_token` out).
232190pub async fn oidc_refresh_token (
233191 bundle : & OidcTokenBundle ,
234192 insecure : bool ,
235193) -> Result < OidcTokenBundle > {
236- let refresh_token = bundle. refresh_token . as_deref ( ) . ok_or_else ( || {
194+ let refresh = bundle. refresh_token . as_deref ( ) . ok_or_else ( || {
237195 miette:: miette!(
238196 "no refresh token available — re-authenticate with: openshell gateway login"
239197 )
240198 } ) ?;
241199
242- let discovery = discover ( & bundle. issuer , insecure) . await ?;
243-
244- let client = BasicClient :: new ( ClientId :: new ( bundle. client_id . clone ( ) ) )
245- . set_token_uri ( TokenUrl :: new ( discovery. token_endpoint ) . into_diagnostic ( ) ?) ;
246-
247- let http = http_client ( insecure) ;
248- let token_response = client
249- . exchange_refresh_token ( & RefreshToken :: new ( refresh_token. to_string ( ) ) )
250- . request_async ( & http)
251- . await
252- . map_err ( |e| miette:: miette!( "token refresh failed: {e}" ) ) ?;
253-
254- let mut refreshed =
255- bundle_from_oauth2_response ( & token_response, & bundle. issuer , & bundle. client_id ) ;
256- if refreshed. refresh_token . is_none ( ) {
257- refreshed. refresh_token . clone_from ( & bundle. refresh_token ) ;
258- }
259- Ok ( refreshed)
200+ let input =
201+ RefreshTokenInput :: new ( refresh, & bundle. issuer , & bundle. client_id ) . with_insecure ( insecure) ;
202+ let output = refresh_token ( & input) . await . into_diagnostic ( ) ?;
203+
204+ Ok ( OidcTokenBundle {
205+ access_token : output. access_token ,
206+ refresh_token : output
207+ . refresh_token
208+ . or_else ( || bundle. refresh_token . clone ( ) ) ,
209+ expires_at : output. expires_at ,
210+ issuer : bundle. issuer . clone ( ) ,
211+ client_id : bundle. client_id . clone ( ) ,
212+ } )
260213}
261214
262215/// Ensure we have a valid OIDC token for the given gateway, refreshing if needed.
0 commit comments