11use crate :: auth:: Auth ;
22use crate :: config:: AppState ;
3- use crate :: db:: { self } ;
3+ use crate :: db;
44use crate :: errors:: { Errors , FieldValidator } ;
55use crate :: util:: { handle_login, Ticket } ;
6+ use anyhow:: { anyhow, Result } ;
7+ use openidconnect:: core:: {
8+ CoreAuthenticationFlow , CoreClient , CoreProviderMetadata , CoreUserInfoClaims ,
9+ } ;
10+
11+ use openidconnect:: {
12+ AccessTokenHash , AuthorizationCode , ClientId , ClientSecret , CsrfToken , IssuerUrl , Nonce ,
13+ PkceCodeChallenge , RedirectUrl , Scope ,
14+ } ;
615use rocket:: http:: { Cookie , Cookies } ;
716use rocket:: request:: Form ;
817use rocket:: response:: Redirect ;
918use rocket:: State ;
1019use rocket_client_addr:: ClientAddr ;
1120use rocket_contrib:: json:: { Json , JsonValue } ;
1221use serde:: Deserialize ;
22+ use std:: env;
23+
24+ use openidconnect:: reqwest:: http_client;
25+
26+ use openidconnect:: { OAuth2TokenResponse , TokenResponse } ;
1327
1428#[ derive( Deserialize ) ]
1529pub struct LoginUser {
@@ -59,9 +73,39 @@ pub fn post_users_login(
5973
6074#[ get( "/login" ) ]
6175pub fn kth_login ( ) -> Redirect {
76+ if let Ok ( oidc) = env:: var ( "USE_OIDC" ) {
77+ println ! ( "use oidc: {}" , oidc) ;
78+ match oidc. as_str ( ) {
79+ "true" => return use_oidc ( ) ,
80+ _ => { }
81+ }
82+ }
6283 Redirect :: to ( "https://login.kth.se/login?service=https://queue.csc.kth.se/auth" )
6384}
6485
86+ #[ derive( FromForm , Default ) ]
87+ pub struct Code {
88+ code : Option < String > ,
89+ state : Option < String > ,
90+ }
91+
92+ #[ get( "/oidc-auth?<params..>" ) ]
93+ pub fn kth_oidc_auth (
94+ mut cookies : Cookies ,
95+ conn : db:: DbConn ,
96+ state : State < AppState > ,
97+ params : Form < Code > ,
98+ client_addr : & ClientAddr ,
99+ ) -> Redirect {
100+ match get_oidc_user ( params) {
101+ Ok ( _) => println ! ( "good login!" ) ,
102+ Err ( err) => {
103+ println ! ( "oidc error: {:?}" , err) ;
104+ }
105+ }
106+ Redirect :: to ( "/" )
107+ }
108+
65109#[ get( "/auth?<params..>" ) ]
66110pub fn kth_auth (
67111 mut cookies : Cookies ,
@@ -82,3 +126,129 @@ pub fn kth_auth(
82126 }
83127 Redirect :: to ( "/" )
84128}
129+
130+ pub fn get_client ( ) -> Result < CoreClient > {
131+ // "https://login.ug.kth.se/adfs/.well-known/openid-configuration".to_string(),
132+ // println!(
133+ // "metadata: {:?}",
134+ // &IssuerUrl::new(
135+ // "https://login.ug.kth.se/adfs/.well-known/openid-configuration".to_string(),
136+ // )?
137+ // );
138+ let provider_metadata = CoreProviderMetadata :: discover (
139+ & IssuerUrl :: new ( "https://login.ug.kth.se/adfs" . to_string ( ) ) ?,
140+ http_client,
141+ ) ?;
142+
143+ // Create an OpenID Connect client by specifying the client ID, client secret, authorization URL
144+ // and token URL.
145+ let application_id = env:: var ( "APPLICATION_ID" ) . expect ( "OIDC need an application ID" ) ;
146+ let client_secret = env:: var ( "CLIENT_SECRET" ) . expect ( "OIDC need a client secret" ) ;
147+ let client = CoreClient :: from_provider_metadata (
148+ provider_metadata,
149+ ClientId :: new ( application_id) ,
150+ Some ( ClientSecret :: new ( client_secret) ) ,
151+ )
152+ // Set the URL the user will be redirected to after the authorization process.
153+ . set_redirect_uri ( RedirectUrl :: new (
154+ "https://queue.csc.kth.se/oidc-auth" . to_string ( ) ,
155+ ) ?) ;
156+ Ok ( client)
157+ }
158+
159+ pub fn use_oidc ( ) -> Redirect {
160+ match generate_redirect ( ) {
161+ Ok ( redirect) => redirect,
162+ Err ( err) => {
163+ println ! ( "oidc error: {:?}" , err) ;
164+
165+ Redirect :: to ( "https://queue.csc.kth.se/failed_login" )
166+ }
167+ }
168+ }
169+
170+ pub fn generate_redirect ( ) -> Result < Redirect > {
171+ println ! ( "generating redirect" ) ;
172+ let client = get_client ( ) ?;
173+
174+ // Generate the full authorization URL.
175+ let ( auth_url, _csrf_token, _nonce) = client
176+ . authorize_url (
177+ CoreAuthenticationFlow :: AuthorizationCode ,
178+ CsrfToken :: new_random,
179+ Nonce :: new_random,
180+ )
181+ // Set the desired scopes.
182+ // .add_scope(Scope::new("openid".to_string()))
183+ . url ( ) ;
184+
185+ // This is the URL you should redirect the user to, in order to trigger the authorization
186+ // process.
187+ println ! ( "Browse to: {}" , auth_url) ;
188+
189+ Ok ( Redirect :: to ( auth_url. to_string ( ) ) )
190+ }
191+
192+ pub fn get_oidc_user ( params : Form < Code > ) -> Result < ( ) > {
193+ let client = get_client ( ) ?;
194+ println ! ( "getting oidc_user" ) ;
195+ let code = params
196+ . code
197+ . as_ref ( )
198+ . ok_or_else ( || anyhow ! ( "got no code in request" ) ) ?;
199+ println ! ( "code: {}" , code) ;
200+ let nonce = Nonce :: new ( "fake_nonce" . to_string ( ) ) ;
201+ // Once the user has been redirected to the redirect URL, you'll have access to the
202+ // authorization code. For security reasons, your code should verify that the `state`
203+ // parameter returned by the server matches `csrf_state`.
204+
205+ // Now you can exchange it for an access token and ID token.
206+ let token_response = client
207+ . exchange_code ( AuthorizationCode :: new ( code. to_string ( ) ) )
208+ . request ( http_client) ?;
209+
210+ println ! ( "Got token response" ) ;
211+ // Extract the ID token claims after verifying its authenticity and nonce.
212+ let id_token = token_response
213+ . id_token ( )
214+ . ok_or_else ( || anyhow ! ( "Server did not return an ID token" ) ) ?;
215+ let claims = id_token. claims ( & client. id_token_verifier ( ) , & nonce) ?;
216+
217+ println ! ( "Got the claims: {:?}" , claims) ;
218+ // Verify the access token hash to ensure that the access token hasn't been substituted for
219+ // another user's.
220+ if let Some ( expected_access_token_hash) = claims. access_token_hash ( ) {
221+ let actual_access_token_hash =
222+ AccessTokenHash :: from_token ( token_response. access_token ( ) , & id_token. signing_alg ( ) ?) ?;
223+ if actual_access_token_hash != * expected_access_token_hash {
224+ return Err ( anyhow ! ( "Invalid access token" ) ) ;
225+ }
226+ }
227+ println ! ( "almost done now!" ) ;
228+
229+ // The authenticated user's identity is now available. See the IdTokenClaims struct for a
230+ // complete listing of the available claims.
231+ println ! (
232+ "User {} with e-mail address {} has authenticated successfully" ,
233+ claims. subject( ) . as_str( ) ,
234+ claims
235+ . email( )
236+ . map( |email| email. as_str( ) )
237+ . unwrap_or( "<not provided>" ) ,
238+ ) ;
239+
240+ // If available, we can use the UserInfo endpoint to request additional information.
241+
242+ // The user_info request uses the AccessToken returned in the token response. To parse custom
243+ // claims, use UserInfoClaims directly (with the desired type parameters) rather than using the
244+ // CoreUserInfoClaims type alias.
245+ let _userinfo: CoreUserInfoClaims = client
246+ . user_info ( token_response. access_token ( ) . to_owned ( ) , None )
247+ . map_err ( |err| anyhow ! ( "No user info endpoint: {:?}" , err) ) ?
248+ . request ( http_client)
249+ . map_err ( |err| anyhow ! ( "Failed requesting user info: {:?}" , err) ) ?;
250+
251+ // See the OAuth2TokenResponse trait for a listing of other available fields such as
252+ // access_token() and refresh_token().
253+ Ok ( ( ) )
254+ }
0 commit comments