client-api: Implement MSC2858 - Multiple SSO Identity Providers
This commit is contained in:
parent
c1693569f1
commit
51951082d5
@ -78,6 +78,7 @@ Breaking changes:
|
|||||||
search::{search_events, search_users}
|
search::{search_events, search_users}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* Change `r0::session::get_login_types::LoginType` to a non-exhaustive enum of structs.
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
|
|
||||||
@ -97,6 +98,9 @@ Improvements:
|
|||||||
get_content_thumbnail
|
get_content_thumbnail
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* Implement MSC2858 - Multiple SSO Identity Providers under the `unstable-pre-spec` feature flag:
|
||||||
|
* Add the `r0::session::get_login_types::{IdentityProvider, IdentityProviderBrand}` types
|
||||||
|
* Add the `r0::session::sso_login_with_provider` endpoint
|
||||||
|
|
||||||
# 0.9.0
|
# 0.9.0
|
||||||
|
|
||||||
|
@ -5,3 +5,4 @@ pub mod login;
|
|||||||
pub mod logout;
|
pub mod logout;
|
||||||
pub mod logout_all;
|
pub mod logout_all;
|
||||||
pub mod sso_login;
|
pub mod sso_login;
|
||||||
|
pub mod sso_login_with_provider;
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
//! [GET /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-login)
|
//! [GET /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-login)
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use ruma_api::ruma_api;
|
use ruma_api::ruma_api;
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
use ruma_identifiers::MxcUri;
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
use ruma_serde::StringEnum;
|
use ruma_serde::StringEnum;
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
|
type JsonObject = serde_json::Map<String, JsonValue>;
|
||||||
|
|
||||||
ruma_api! {
|
ruma_api! {
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -18,7 +27,6 @@ ruma_api! {
|
|||||||
|
|
||||||
response: {
|
response: {
|
||||||
/// The homeserver's supported login types.
|
/// The homeserver's supported login types.
|
||||||
#[serde(with = "login_type_list_serde")]
|
|
||||||
pub flows: Vec<LoginType>,
|
pub flows: Vec<LoginType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,51 +48,327 @@ impl Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An authentication mechanism.
|
/// An authentication mechanism.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[serde(untagged)]
|
||||||
pub enum LoginType {
|
pub enum LoginType {
|
||||||
/// A password is supplied to authenticate.
|
/// A password is supplied to authenticate.
|
||||||
#[ruma_enum(rename = "m.login.password")]
|
Password(PasswordLoginType),
|
||||||
Password,
|
|
||||||
|
|
||||||
/// Token-based login.
|
/// Token-based login.
|
||||||
#[ruma_enum(rename = "m.login.token")]
|
Token(TokenLoginType),
|
||||||
Token,
|
|
||||||
|
|
||||||
/// SSO-based login.
|
/// SSO-based login.
|
||||||
#[ruma_enum(rename = "m.login.sso")]
|
Sso(SsoLoginType),
|
||||||
Sso,
|
|
||||||
|
|
||||||
|
/// Custom login type.
|
||||||
|
#[doc(hidden)]
|
||||||
|
_Custom(CustomLoginType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoginType {
|
||||||
|
/// Creates a `LoginType` with the given `login_type` string and data.
|
||||||
|
///
|
||||||
|
/// Prefer to use the public variants of `LoginType` where possible; this constructor is meant
|
||||||
|
/// be used for unsupported login types only and does not allow setting arbitrary data for
|
||||||
|
/// supported ones.
|
||||||
|
pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result<Self> {
|
||||||
|
fn from_json_object<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
|
||||||
|
serde_json::from_value(JsonValue::Object(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match login_type {
|
||||||
|
"m.login.password" => Self::Password(from_json_object(data)?),
|
||||||
|
"m.login.token" => Self::Token(from_json_object(data)?),
|
||||||
|
"m.login.sso" => Self::Sso(from_json_object(data)?),
|
||||||
|
_ => Self::_Custom(CustomLoginType { type_: login_type.to_owned(), data }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the `login_type` string.
|
||||||
|
pub fn login_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Password(_) => "m.login.password",
|
||||||
|
Self::Token(_) => "m.login.token",
|
||||||
|
Self::Sso(_) => "m.login.sso",
|
||||||
|
Self::_Custom(c) => &c.type_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the associated data.
|
||||||
|
///
|
||||||
|
/// Prefer to use the public variants of `LoginType` where possible; this method is meant to
|
||||||
|
/// be used for unsupported login types only.
|
||||||
|
pub fn data(&self) -> Cow<'_, JsonObject> {
|
||||||
|
fn serialize<T: Serialize>(obj: &T) -> JsonObject {
|
||||||
|
match serde_json::to_value(obj).expect("login type serialization to succeed") {
|
||||||
|
JsonValue::Object(obj) => obj,
|
||||||
|
_ => panic!("all login types must serialize to objects"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Password(d) => Cow::Owned(serialize(d)),
|
||||||
|
Self::Token(d) => Cow::Owned(serialize(d)),
|
||||||
|
Self::Sso(d) => Cow::Owned(serialize(d)),
|
||||||
|
Self::_Custom(c) => Cow::Borrowed(&c.data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The payload for password login.
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[serde(tag = "type", rename = "m.login.password")]
|
||||||
|
pub struct PasswordLoginType {}
|
||||||
|
|
||||||
|
impl PasswordLoginType {
|
||||||
|
/// Creates a new `PasswordLoginType`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The payload for token-based login.
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[serde(tag = "type", rename = "m.login.token")]
|
||||||
|
pub struct TokenLoginType {}
|
||||||
|
|
||||||
|
impl TokenLoginType {
|
||||||
|
/// Creates a new `PasswordLoginType`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The payload for SSO login.
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[serde(tag = "type", rename = "m.login.sso")]
|
||||||
|
pub struct SsoLoginType {
|
||||||
|
/// The identity provider choices.
|
||||||
|
///
|
||||||
|
/// This uses the unstable prefix in
|
||||||
|
/// [MSC2858](https://github.com/matrix-org/matrix-doc/pull/2858).
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
rename = "org.matrix.msc2858.identity_providers",
|
||||||
|
skip_serializing_if = "Vec::is_empty"
|
||||||
|
)]
|
||||||
|
pub identity_providers: Vec<IdentityProvider>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SsoLoginType {
|
||||||
|
/// Creates a new `PasswordLoginType`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An SSO login identity provider.
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub struct IdentityProvider {
|
||||||
|
/// The ID of the provider.
|
||||||
|
id: String,
|
||||||
|
|
||||||
|
/// The display name of the provider.
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
/// The icon for the provider.
|
||||||
|
icon: Option<MxcUri>,
|
||||||
|
|
||||||
|
/// The brand identifier for the provider.
|
||||||
|
brand: Option<IdentityProviderBrand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
impl IdentityProvider {
|
||||||
|
/// Creates an `IdentityProvider` with the given `id` and `name`.
|
||||||
|
pub fn new(id: String, name: String) -> Self {
|
||||||
|
Self { id, name, icon: None, brand: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An SSO login identity provider brand identifier.
|
||||||
|
///
|
||||||
|
/// This uses the unstable prefix in
|
||||||
|
/// [MSC2858](https://github.com/matrix-org/matrix-doc/pull/2858).
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub enum IdentityProviderBrand {
|
||||||
|
/// The [Apple] brand.
|
||||||
|
///
|
||||||
|
/// [Apple]: https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
|
||||||
|
#[ruma_enum(rename = "org.matrix.apple")]
|
||||||
|
Apple,
|
||||||
|
|
||||||
|
/// The [Facebook](https://developers.facebook.com/docs/facebook-login/web/login-button/) brand.
|
||||||
|
#[ruma_enum(rename = "org.matrix.facebook")]
|
||||||
|
Facebook,
|
||||||
|
|
||||||
|
/// The [GitHub](https://github.com/logos) brand.
|
||||||
|
#[ruma_enum(rename = "org.matrix.github")]
|
||||||
|
GitHub,
|
||||||
|
|
||||||
|
/// The [GitLab](https://about.gitlab.com/press/press-kit/) brand.
|
||||||
|
#[ruma_enum(rename = "org.matrix.gitlab")]
|
||||||
|
GitLab,
|
||||||
|
|
||||||
|
/// The [Google](https://developers.google.com/identity/branding-guidelines) brand.
|
||||||
|
#[ruma_enum(rename = "org.matrix.google")]
|
||||||
|
Google,
|
||||||
|
|
||||||
|
/// The [Twitter] brand.
|
||||||
|
///
|
||||||
|
/// [Twitter]: https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1
|
||||||
|
#[ruma_enum(rename = "org.matrix.twitter")]
|
||||||
|
Twitter,
|
||||||
|
|
||||||
|
/// A custom brand.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
_Custom(String),
|
_Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
mod login_type_list_serde;
|
/// A custom login payload.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub struct CustomLoginType {
|
||||||
|
/// A custom type
|
||||||
|
///
|
||||||
|
/// This field is named `type_` instead of `type` because the latter is a reserved
|
||||||
|
/// keyword in Rust.
|
||||||
|
#[serde(rename = "override")]
|
||||||
|
pub type_: String,
|
||||||
|
|
||||||
|
/// Remaining type content
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub data: JsonObject,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod login_type_serde;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use matches::assert_matches;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::Deserialize;
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
use serde_json::to_value as to_json_value;
|
||||||
use serde_json::{from_value as from_json_value, json};
|
use serde_json::{from_value as from_json_value, json};
|
||||||
|
|
||||||
use super::{login_type_list_serde, LoginType};
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
use super::{IdentityProvider, IdentityProviderBrand, SsoLoginType, TokenLoginType};
|
||||||
|
use super::{LoginType, PasswordLoginType};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
struct Foo {
|
struct Foo {
|
||||||
#[serde(with = "login_type_list_serde")]
|
|
||||||
pub flows: Vec<LoginType>,
|
pub flows: Vec<LoginType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_login_type() {
|
fn deserialize_password_login_type() {
|
||||||
assert_matches!(
|
assert_eq!(
|
||||||
from_json_value::<Foo>(json!({
|
from_json_value::<Foo>(json!({
|
||||||
"flows": [
|
"flows": [
|
||||||
{ "type": "m.login.password" }
|
{ "type": "m.login.password" }
|
||||||
],
|
],
|
||||||
})),
|
}))
|
||||||
Ok(Foo { flows })
|
.unwrap(),
|
||||||
if flows.len() == 1
|
Foo { flows: vec![LoginType::Password(PasswordLoginType {})] }
|
||||||
&& flows[0] == LoginType::Password
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
fn deserialize_sso_login_type() {
|
||||||
|
let foo = from_json_value::<Foo>(json!({
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"type": "m.login.sso",
|
||||||
|
"org.matrix.msc2858.identity_providers": [
|
||||||
|
{
|
||||||
|
"id": "oidc-gitlab",
|
||||||
|
"name": "GitLab",
|
||||||
|
"icon": "mxc://localhost/gitlab-icon",
|
||||||
|
"brand": "org.matrix.gitlab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom",
|
||||||
|
"name": "Custom",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
foo,
|
||||||
|
Foo {
|
||||||
|
flows: vec![LoginType::Sso(SsoLoginType {
|
||||||
|
identity_providers: vec![
|
||||||
|
IdentityProvider {
|
||||||
|
id: "oidc-gitlab".into(),
|
||||||
|
name: "GitLab".into(),
|
||||||
|
icon: Some("mxc://localhost/gitlab-icon".into()),
|
||||||
|
brand: Some(IdentityProviderBrand::GitLab)
|
||||||
|
},
|
||||||
|
IdentityProvider {
|
||||||
|
id: "custom".into(),
|
||||||
|
name: "Custom".into(),
|
||||||
|
icon: None,
|
||||||
|
brand: None
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
fn serialize_sso_login_type() {
|
||||||
|
let foo = to_json_value(Foo {
|
||||||
|
flows: vec![
|
||||||
|
LoginType::Token(TokenLoginType {}),
|
||||||
|
LoginType::Sso(SsoLoginType {
|
||||||
|
identity_providers: vec![IdentityProvider {
|
||||||
|
id: "oidc-github".into(),
|
||||||
|
name: "GitHub".into(),
|
||||||
|
icon: Some("mxc://localhost/github-icon".into()),
|
||||||
|
brand: Some(IdentityProviderBrand::GitHub),
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
foo,
|
||||||
|
json!({
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"type": "m.login.token"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "m.login.sso",
|
||||||
|
"org.matrix.msc2858.identity_providers": [
|
||||||
|
{
|
||||||
|
"id": "oidc-github",
|
||||||
|
"name": "GitHub",
|
||||||
|
"icon": "mxc://localhost/github-icon",
|
||||||
|
"brand": "org.matrix.github"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
||||||
|
|
||||||
use super::LoginType;
|
|
||||||
|
|
||||||
pub fn serialize<S>(login_types: &[LoginType], serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct Wrap<'a> {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
inner: &'a LoginType,
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer.collect_seq(login_types.iter().map(|ty| Wrap { inner: ty }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<LoginType>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Wrap {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
inner: LoginType,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could be optimized by using a visitor, but that's a bunch of extra code
|
|
||||||
let vec = Vec::<Wrap>::deserialize(deserializer)?;
|
|
||||||
Ok(vec.into_iter().map(|w| w.inner).collect())
|
|
||||||
}
|
|
@ -0,0 +1,31 @@
|
|||||||
|
use serde::{de, Deserialize};
|
||||||
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
|
use ruma_events::from_raw_json_value;
|
||||||
|
|
||||||
|
use super::LoginType;
|
||||||
|
|
||||||
|
/// Helper struct to determine the type from a `serde_json::value::RawValue`
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct LoginTypeDeHelper {
|
||||||
|
/// The login type field
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
type_: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::Deserialize<'de> for LoginType {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
||||||
|
let LoginTypeDeHelper { type_ } = from_raw_json_value(&json)?;
|
||||||
|
|
||||||
|
Ok(match type_.as_ref() {
|
||||||
|
"m.login.password" => Self::Password(from_raw_json_value(&json)?),
|
||||||
|
"m.login.token" => Self::Token(from_raw_json_value(&json)?),
|
||||||
|
"m.login.sso" => Self::Sso(from_raw_json_value(&json)?),
|
||||||
|
_ => Self::_Custom(from_raw_json_value(&json)?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
71
ruma-client-api/src/r0/session/sso_login_with_provider.rs
Normal file
71
ruma-client-api/src/r0/session/sso_login_with_provider.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//! [GET /_matrix/client/r0/login/sso/redirect/{idp_id}](https://github.com/matrix-org/matrix-doc/blob/master/proposals/2858-Multiple-SSO-Identity-Providers.md)
|
||||||
|
//!
|
||||||
|
//! This uses the unstable prefix in [MSC2858](https://github.com/matrix-org/matrix-doc/pull/2858).
|
||||||
|
#![cfg(feature = "unstable-pre-spec")]
|
||||||
|
#![cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||||
|
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata: {
|
||||||
|
description: "Get the SSO login identity provider url.",
|
||||||
|
method: GET,
|
||||||
|
name: "sso_login_with_provider",
|
||||||
|
path: "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect/:idp_id",
|
||||||
|
rate_limited: false,
|
||||||
|
authentication: None,
|
||||||
|
}
|
||||||
|
|
||||||
|
request: {
|
||||||
|
/// The ID of the provider to use for SSO login.
|
||||||
|
#[ruma_api(path)]
|
||||||
|
pub idp_id: &'a str,
|
||||||
|
|
||||||
|
/// URL to which the homeserver should return the user after completing
|
||||||
|
/// authentication with the SSO identity provider.
|
||||||
|
#[ruma_api(query)]
|
||||||
|
#[serde(rename = "redirectUrl")]
|
||||||
|
pub redirect_url: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
response: {
|
||||||
|
/// Redirect URL to the SSO identity provider.
|
||||||
|
#[ruma_api(header = LOCATION)]
|
||||||
|
pub location: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
error: crate::Error
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> {
|
||||||
|
/// Creates a new `Request` with the given identity provider ID and redirect URL.
|
||||||
|
pub fn new(idp_id: &'a str, redirect_url: &'a str) -> Self {
|
||||||
|
Self { idp_id, redirect_url }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
/// Creates a new `Response` with the given SSO URL.
|
||||||
|
pub fn new(location: String) -> Self {
|
||||||
|
Self { location }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ruma_api::OutgoingRequest;
|
||||||
|
|
||||||
|
use super::Request;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_sso_login_with_provider_request_uri() {
|
||||||
|
let req = Request { idp_id: "provider", redirect_url: "https://example.com/sso" }
|
||||||
|
.try_into_http_request("https://homeserver.tld", None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
req.uri().to_string(),
|
||||||
|
"https://homeserver.tld/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect/provider?redirectUrl=https%3A%2F%2Fexample.com%2Fsso"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user