1use jsonwebtoken::{DecodingKey, TokenData, Validation};
2use oauth2::basic::BasicClient;
3use oauth2::reqwest::async_http_client;
4use oauth2::{
5 AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, EmptyExtraTokenFields,
6 RedirectUrl, Scope, StandardTokenResponse, TokenUrl,
7};
8
9use crate::error::Error;
10use crate::esi::{client, LibResult};
11
12const AUTHORIZE_URL: &str = "https://login.eveonline.com/v2/oauth/authorize";
13const TOKEN_URL: &str = "https://login.eveonline.com/v2/oauth/token";
14const AUTHORIZATION_SERVER_URL: &str =
15 "https://login.eveonline.com/.well-known/oauth-authorization-server";
16const LOGIN_URL: &str = "https://login.eveonline.com";
17
18#[derive(Debug, serde::Serialize, serde::Deserialize)]
19pub struct AuthenticationData {
20 pub login_url: String,
21 pub state: String,
22}
23
24#[derive(Debug, serde::Serialize, serde::Deserialize)]
25pub struct EveSsoMetaData {
26 pub authorization_endpoint: String,
27 pub code_challenge_methods_supported: Vec<String>,
28 pub issuer: String,
29 pub jwks_uri: String,
30 pub response_types_supported: Vec<String>,
31 pub revocation_endpoint: String,
32 pub revocation_endpoint_auth_methods_supported: Vec<String>,
33 pub token_endpoint: String,
34 pub token_endpoint_auth_methods_supported: Vec<String>,
35 pub token_endpoint_auth_signing_alg_values_supported: Vec<String>,
36}
37
38#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
39pub struct EveJwtKeys {
40 #[serde(rename = "SkipUnresolvedJsonWebKeys")]
41 pub skip_unresolved_json_web_keys: bool,
42 pub keys: Vec<EveJwtKey>,
43}
44
45#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
46#[serde(tag = "alg")]
47pub enum EveJwtKey {
48 RS256 {
49 e: String,
50 kid: String,
51 kty: String,
52 n: String,
53 r#use: String,
54 },
55 ES256 {
56 crv: String,
57 kid: String,
58 kty: String,
59 r#use: String,
60 x: String,
61 y: String,
62 },
63}
64
65#[derive(Debug, serde::Serialize, serde::Deserialize)]
66pub struct EveJwtClaims {
67 pub scp: Option<Vec<String>>,
68 pub jti: String,
69 pub kid: String,
70 pub sub: String,
71 pub azp: String,
72 pub tenant: String,
73 pub tier: String,
74 pub region: String,
75 pub aud: Vec<String>,
76 pub name: String,
77 pub owner: String,
78 pub exp: u64,
79 pub iat: u64,
80 pub iss: String,
81}
82
83pub fn create_login_url(
84 client_id: String,
85 client_secret: String,
86 callback_url: String,
87 scopes: Vec<String>,
88) -> LibResult<AuthenticationData> {
89 fn convert_scopes(scopes: Vec<String>) -> Vec<Scope> {
90 scopes.iter().map(|s| Scope::new(s.clone())).collect()
91 }
92
93 let client = BasicClient::new(
94 ClientId::new(client_id),
95 Some(ClientSecret::new(client_secret)),
96 AuthUrl::new(AUTHORIZE_URL.to_string())?,
97 Some(TokenUrl::new(TOKEN_URL.to_string())?),
98 )
99 .set_redirect_uri(RedirectUrl::new(callback_url)?);
100
101 let scopes = convert_scopes(scopes);
102
103 let (eve_oauth_url, csrf_token) = client
104 .authorize_url(CsrfToken::new_random)
105 .add_scopes(scopes)
106 .url();
107
108 Ok(AuthenticationData {
109 login_url: eve_oauth_url.to_string(),
110 state: csrf_token.secret().to_string(),
111 })
112}
113
114pub async fn get_token(
115 client_id: String,
116 client_secret: String,
117 code: String,
118) -> LibResult<StandardTokenResponse<EmptyExtraTokenFields, oauth2::basic::BasicTokenType>> {
119 let client = BasicClient::new(
120 ClientId::new(client_id),
121 Some(ClientSecret::new(client_secret)),
122 AuthUrl::new(AUTHORIZE_URL.to_string())?,
123 Some(TokenUrl::new(TOKEN_URL.to_string())?),
124 );
125
126 match client
127 .exchange_code(AuthorizationCode::new(code.to_string()))
128 .request_async(async_http_client)
129 .await
130 {
131 Ok(token) => Ok(token),
132 Err(err) => return Err(Error::new(500, err.to_string())),
133 }
134}
135
136#[derive(Debug, serde::Serialize, serde::Deserialize)]
158pub struct RefreshTokenResponse {
159 pub access_token: String,
160 pub token_type: String,
161 pub expires_in: i64,
162 pub refresh_token: String,
163}
164
165pub async fn token_refresh(token_id: &str) -> LibResult<RefreshTokenResponse> {
166 println!("Token ID: {}", token_id);
167 Err(Error::new(500, "Failed to refresh token".to_string()))
169 }
184
185pub async fn validate_token(token: &str) -> LibResult<TokenData<EveJwtClaims>> {
186 async fn get_eve_jwt_keys() -> LibResult<EveJwtKeys> {
187 let sso_meta_data_url = AUTHORIZATION_SERVER_URL;
188
189 let res: EveSsoMetaData = client().get(sso_meta_data_url).send().await?.json().await?;
190
191 let response = client().get(res.jwks_uri).send().await?.json().await?;
192
193 Ok(response)
194 }
195
196 let jwk_keys = get_eve_jwt_keys().await?;
197 let jwk_key = match select_key(jwk_keys.keys) {
198 Some(key) => key,
199 None => return Err(Error::new(500, "Failed to find RS256 key".to_string())),
200 };
201
202 let jwk_n: String;
203 let jwk_e: String;
204
205 if let EveJwtKey::RS256 {
206 e,
207 kid: _,
208 kty: _,
209 n,
210 r#use: _,
211 } = jwk_key
212 {
213 jwk_n = n;
214 jwk_e = e;
215 } else {
216 return Err(Error::new(500, "Failed to find RS256 key".to_string()));
217 }
218
219 let mut validation = Validation::new(jsonwebtoken::Algorithm::RS256);
220 validation.set_audience(&["EVE Online"]);
221 validation.set_issuer(&[LOGIN_URL]);
222
223 let decoding_key = &DecodingKey::from_rsa_components(&jwk_n, &jwk_e)?;
224
225 let token = jsonwebtoken::decode::<EveJwtClaims>(&token, decoding_key, &validation)?;
226
227 Ok(token)
228}
229
230fn select_key(keys: Vec<EveJwtKey>) -> Option<EveJwtKey> {
231 for key in keys {
232 if let EveJwtKey::RS256 {
233 e: _,
234 kid: _,
235 kty: _,
236 n: _,
237 r#use: _,
238 } = &key
239 {
240 return Some(key);
241 }
242 }
243
244 None
245}