Skip to content

Commit 1277aff

Browse files
committed
error handling: auth rejected sketch
connecting the bits
1 parent 620ba01 commit 1277aff

File tree

4 files changed

+64
-23
lines changed

4 files changed

+64
-23
lines changed

who-am-i/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ mod oauth;
33
mod server;
44

55
pub use expiring_task_map::ExpiringTaskMap;
6-
pub use oauth::{OAuth, OauthCallbackParams, ResolveHandleError};
6+
pub use oauth::{OAuth, OAuthCallbackParams, OAuthCompleteError, ResolveHandleError};
77
pub use server::serve;

who-am-i/src/oauth.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct CallbackErrorParams {
2727

2828
#[derive(Debug, Deserialize)]
2929
#[serde(untagged)]
30-
pub enum OauthCallbackParams {
30+
pub enum OAuthCallbackParams {
3131
Granted(CallbackParams),
3232
Failed(CallbackErrorParams),
3333
}
@@ -58,15 +58,13 @@ pub enum AuthSetupError {
5858
pub struct AuthStartError(#[from] atrium_oauth::Error);
5959

6060
#[derive(Debug, Error)]
61-
pub enum AuthCompleteError {
61+
pub enum OAuthCompleteError {
6262
#[error("the user denied request: {description:?} (from {issuer:?})")]
6363
Denied {
6464
description: Option<String>,
6565
issuer: Option<String>,
6666
},
67-
#[error(
68-
"the request was denied for another reason: {error}: {description:?} (from {issuer:?})"
69-
)]
67+
#[error("the request failed: {error}: {description:?} (from {issuer:?})")]
7068
Failed {
7169
error: String,
7270
description: Option<String>,
@@ -135,17 +133,17 @@ impl OAuth {
135133
}
136134

137135
/// Finally, resolve the oauth flow to a verified DID
138-
pub async fn complete(&self, params: OauthCallbackParams) -> Result<Did, AuthCompleteError> {
136+
pub async fn complete(&self, params: OAuthCallbackParams) -> Result<Did, OAuthCompleteError> {
139137
let params = match params {
140-
OauthCallbackParams::Granted(params) => params,
141-
OauthCallbackParams::Failed(p) if p.error == "access_denied" => {
142-
return Err(AuthCompleteError::Denied {
138+
OAuthCallbackParams::Granted(params) => params,
139+
OAuthCallbackParams::Failed(p) if p.error == "access_denied" => {
140+
return Err(OAuthCompleteError::Denied {
143141
description: p.error_description.clone(),
144142
issuer: p.iss.clone(),
145143
});
146144
}
147-
OauthCallbackParams::Failed(p) => {
148-
return Err(AuthCompleteError::Failed {
145+
OAuthCallbackParams::Failed(p) => {
146+
return Err(OAuthCompleteError::Failed {
149147
error: p.error.clone(),
150148
description: p.error_description.clone(),
151149
issuer: p.iss.clone(),
@@ -156,9 +154,9 @@ impl OAuth {
156154
.client
157155
.callback(params)
158156
.await
159-
.map_err(AuthCompleteError::CallbackFailed)?;
157+
.map_err(OAuthCompleteError::CallbackFailed)?;
160158
let Some(did) = session.did().await else {
161-
return Err(AuthCompleteError::NoDid);
159+
return Err(OAuthCompleteError::NoDid);
162160
};
163161
Ok(did)
164162
}

who-am-i/src/server.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ use atrium_api::types::string::Did;
22
use axum::{
33
Router,
44
extract::{FromRef, Query, State},
5-
http::header::{HeaderMap, REFERER},
6-
response::{Html, IntoResponse, Json, Redirect},
5+
http::{
6+
StatusCode,
7+
header::{HeaderMap, REFERER},
8+
},
9+
response::{Html, IntoResponse, Json, Redirect, Response},
710
routing::get,
811
};
912
use axum_extra::extract::cookie::{Cookie, Key, SameSite, SignedCookieJar};
@@ -19,7 +22,7 @@ use tokio::net::TcpListener;
1922
use tokio_util::sync::CancellationToken;
2023
use url::Url;
2124

22-
use crate::{ExpiringTaskMap, OAuth, OauthCallbackParams, ResolveHandleError};
25+
use crate::{ExpiringTaskMap, OAuth, OAuthCallbackParams, OAuthCompleteError, ResolveHandleError};
2326

2427
const FAVICON: &[u8] = include_bytes!("../static/favicon.ico");
2528
const INDEX_HTML: &str = include_str!("../static/index.html");
@@ -190,6 +193,30 @@ async fn start_oauth(
190193
(jar, Redirect::to(&auth_url))
191194
}
192195

196+
impl OAuthCompleteError {
197+
fn to_error_response(&self, engine: AppEngine) -> Response {
198+
let (_level, _desc) = match self {
199+
OAuthCompleteError::Denied { .. } => {
200+
let status = StatusCode::FORBIDDEN;
201+
return (status, RenderHtml("auth-fail", engine, json!({}))).into_response();
202+
}
203+
OAuthCompleteError::Failed { .. } => (
204+
"error",
205+
"Something went wrong while requesting permission, sorry!",
206+
),
207+
OAuthCompleteError::CallbackFailed(_) => (
208+
"error",
209+
"Something went wrong after permission was granted, sorry!",
210+
),
211+
OAuthCompleteError::NoDid => (
212+
"error",
213+
"Something went wrong when trying to confirm your identity, sorry!",
214+
),
215+
};
216+
todo!();
217+
}
218+
}
219+
193220
async fn complete_oauth(
194221
State(AppState {
195222
engine,
@@ -198,11 +225,12 @@ async fn complete_oauth(
198225
shutdown,
199226
..
200227
}): State<AppState>,
201-
Query(params): Query<OauthCallbackParams>,
228+
Query(params): Query<OAuthCallbackParams>,
202229
jar: SignedCookieJar,
203-
) -> (SignedCookieJar, impl IntoResponse) {
204-
let Ok(did) = oauth.complete(params).await else {
205-
panic!("failed to do client callback");
230+
) -> Result<(SignedCookieJar, impl IntoResponse), Response> {
231+
let did = match oauth.complete(params).await {
232+
Ok(did) => did,
233+
Err(e) => return Err(e.to_error_response(engine)),
206234
};
207235

208236
let cookie = Cookie::build((DID_COOKIE_KEY, did.to_string()))
@@ -222,7 +250,7 @@ async fn complete_oauth(
222250
shutdown.child_token(),
223251
);
224252

225-
(
253+
Ok((
226254
jar,
227255
RenderHtml(
228256
"authorized",
@@ -232,5 +260,5 @@ async fn complete_oauth(
232260
"fetch_key": fetch_key,
233261
}),
234262
),
235-
)
263+
))
236264
}

who-am-i/templates/auth-fail.hbs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{{#*inline "main"}}
2+
<p>
3+
Share your identity with
4+
<span class="parent-host">{{ parent_host }}</span>?
5+
</p>
6+
7+
<div id="user-info">
8+
<div id="action">
9+
auth failed.
10+
</form>
11+
</div>
12+
13+
{{/inline}}
14+
15+
{{#> prompt-base}}{{/prompt-base}}

0 commit comments

Comments
 (0)