alibi_plugins/oauth_provider/
continuation.rs1use super::OAuthProviderPlugin;
3use alibi_core::endpoint::{EndpointResponse, ServerEndpoint};
4use alibi_core::session::SessionRequest;
5use alibi_core::utils::json::JsValue;
6use alibi_core::{AuthContext, AuthError, AuthResult, AuthSchema};
7use serde_json::{Value, json};
8#[derive(serde::Deserialize)]
9pub(super) struct ContinuationInput {
10 selected: Option<bool>,
11 created: Option<bool>,
12 #[serde(rename = "postLogin")]
13 post_login: Option<bool>,
14 oauth_query: Option<String>,
15}
16pub(super) fn validate(input: Option<&JsValue>) -> AuthResult<JsValue> {
17 use crate::authentication_helpers::{JsonField, JsonFieldKind};
18 crate::endpoint::validate_fields(
19 input,
20 "body",
21 &[
22 JsonField {
23 name: "selected",
24 kind: JsonFieldKind::Boolean,
25 required: false,
26 },
27 JsonField {
28 name: "created",
29 kind: JsonFieldKind::Boolean,
30 required: false,
31 },
32 JsonField {
33 name: "postLogin",
34 kind: JsonFieldKind::Boolean,
35 required: false,
36 },
37 JsonField::string("oauth_query", false),
38 ],
39 )
40}
41pub(super) fn interaction_query(supplied: &str) -> serde_json::Map<String, Value> {
42 let mut query = serde_json::Map::new();
43 for (key, value) in url::form_urlencoded::parse(supplied.as_bytes()) {
44 if ["sig", "exp", "ba_iat", "ba_pl"].contains(&key.as_ref()) {
45 continue;
46 }
47 match query.entry(key.into_owned()) {
48 serde_json::map::Entry::Vacant(entry) => {
49 _ = entry.insert(json!(value));
50 }
51 serde_json::map::Entry::Occupied(mut entry) => {
52 let previous = entry.get().clone();
53 if let Some(values) = entry.get_mut().as_array_mut() {
54 values.push(json!(value));
55 } else {
56 _ = entry.insert(json!([previous, value]));
57 }
58 }
59 }
60 }
61 query
62}
63pub(super) fn post_login_clearance(supplied: &str) -> Option<String> {
66 url::form_urlencoded::parse(supplied.as_bytes())
67 .find(|(key, _)| key == "ba_pl")
68 .map(|(_, value)| value.into_owned())
69}
70impl OAuthProviderPlugin {
71 pub fn oauth2_continue_endpoint(
73 input: &impl serde::Serialize,
74 ) -> AuthResult<ServerEndpoint<Value>> {
75 ServerEndpoint::new("oauth-provider", "oauth2Continue").with_body(input)
76 }
77 pub(super) async fn continue_authorization<S: AuthSchema>(
78 &self,
79 input: ContinuationInput,
80 req: &impl SessionRequest,
81 ctx: &AuthContext<S>,
82 ) -> AuthResult<EndpointResponse> {
83 let supplied = input.oauth_query.as_deref().unwrap_or("");
84 if !supplied.is_empty()
85 && !super::authorization::verify_authorization_query(
86 supplied,
87 ctx.config.current_secret(),
88 )?
89 {
90 return Ok(EndpointResponse::error(AuthError::Api {
91 status: 400,
92 code: None,
93 message: String::new(),
94 })
95 .with_error_body(alibi_core::utils::json::parse_value(
96 r#"{"error":"invalid_signature"}"#,
97 )?));
98 }
99 _ = Box::pin(crate::helpers::ordinary_session(req, ctx)).await?;
100 let remove = if input.selected == Some(true) {
101 Some("select_account")
102 } else if input.created == Some(true) {
103 Some("create")
104 } else if input.post_login == Some(true) {
105 None
106 } else {
107 return super::clients::status_protocol_error(
108 400,
109 "invalid_request",
110 "Missing parameters",
111 );
112 };
113 if supplied.is_empty() {
114 return super::clients::status_protocol_error(
115 400,
116 "invalid_request",
117 "missing oauth query",
118 );
119 }
120 let mut query = interaction_query(supplied);
121 let clearance = if remove.is_none() {
122 post_login_clearance(supplied)
123 } else {
124 None
125 };
126 if let Some(remove) = remove
127 && let Some(prompt) = query.get("prompt").and_then(Value::as_str)
128 {
129 let mut prompts = prompt.split(' ').collect::<Vec<_>>();
130 if let Some(index) = prompts.iter().position(|prompt| *prompt == remove) {
131 _ = prompts.remove(index);
132 if prompts.is_empty() {
133 _ = query.remove("prompt");
134 } else {
135 _ = query.insert("prompt".into(), json!(prompts.join(" ")));
136 }
137 }
138 }
139 Box::pin(self.resume_authorization(query, req, ctx, clearance)).await
140 }
141}