You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Host a Swagger UI both in the static documentation and by the server
This commit is contained in:
@ -14,13 +14,23 @@
|
||||
|
||||
use aide::{
|
||||
axum::ApiRouter,
|
||||
openapi::{OAuth2Flow, OAuth2Flows, OpenApi, SecurityScheme, Server, ServerVariable},
|
||||
openapi::{OAuth2Flow, OAuth2Flows, OpenApi, SecurityScheme, Server},
|
||||
};
|
||||
use axum::{
|
||||
extract::{FromRef, FromRequestParts, State},
|
||||
http::HeaderName,
|
||||
response::Html,
|
||||
Json, Router,
|
||||
};
|
||||
use axum::{extract::FromRequestParts, Json, Router};
|
||||
use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
|
||||
use indexmap::IndexMap;
|
||||
use mas_axum_utils::FancyError;
|
||||
use mas_http::CorsLayerExt;
|
||||
use mas_router::{OAuth2AuthorizationEndpoint, OAuth2TokenEndpoint, SimpleRoute};
|
||||
use mas_router::{
|
||||
ApiDoc, ApiDocCallback, OAuth2AuthorizationEndpoint, OAuth2TokenEndpoint, Route, SimpleRoute,
|
||||
UrlBuilder,
|
||||
};
|
||||
use mas_templates::{ApiDocContext, Templates};
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
|
||||
mod call_context;
|
||||
@ -35,6 +45,8 @@ pub fn router<S>() -> (OpenApi, Router<S>)
|
||||
where
|
||||
S: Clone + Send + Sync + 'static,
|
||||
CallContext: FromRequestParts<S>,
|
||||
Templates: FromRef<S>,
|
||||
UrlBuilder: FromRef<S>,
|
||||
{
|
||||
let mut api = OpenApi::default();
|
||||
let router = ApiRouter::<S>::new()
|
||||
@ -70,17 +82,6 @@ where
|
||||
},
|
||||
)
|
||||
.security_requirement_scopes("oauth2", ["urn:mas:admin"])
|
||||
.server(Server {
|
||||
url: "{base}".to_owned(),
|
||||
variables: IndexMap::from([(
|
||||
"base".to_owned(),
|
||||
ServerVariable {
|
||||
default: "/".to_owned(),
|
||||
..ServerVariable::default()
|
||||
},
|
||||
)]),
|
||||
..Server::default()
|
||||
})
|
||||
});
|
||||
|
||||
let router = router
|
||||
@ -88,16 +89,55 @@ where
|
||||
.route(
|
||||
"/api/spec.json",
|
||||
axum::routing::get({
|
||||
let res = Json(api.clone());
|
||||
move || std::future::ready(res.clone())
|
||||
let api = api.clone();
|
||||
move |State(url_builder): State<UrlBuilder>| {
|
||||
// Let's set the servers to the HTTP base URL
|
||||
let mut api = api.clone();
|
||||
api.servers = vec![Server {
|
||||
url: url_builder.http_base().to_string(),
|
||||
..Server::default()
|
||||
}];
|
||||
|
||||
std::future::ready(Json(api))
|
||||
}
|
||||
}),
|
||||
)
|
||||
// Serve the Swagger API reference
|
||||
.route(ApiDoc::route(), axum::routing::get(swagger))
|
||||
.route(
|
||||
ApiDocCallback::route(),
|
||||
axum::routing::get(swagger_callback),
|
||||
)
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods(Any)
|
||||
.allow_otel_headers([AUTHORIZATION, ACCEPT, CONTENT_TYPE]),
|
||||
.allow_otel_headers([
|
||||
AUTHORIZATION,
|
||||
ACCEPT,
|
||||
CONTENT_TYPE,
|
||||
// Swagger will send this header, so we have to allow it to avoid CORS errors
|
||||
HeaderName::from_static("x-requested-with"),
|
||||
]),
|
||||
);
|
||||
|
||||
(api, router)
|
||||
}
|
||||
|
||||
async fn swagger(
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
State(templates): State<Templates>,
|
||||
) -> Result<Html<String>, FancyError> {
|
||||
let ctx = ApiDocContext::from_url_builder(&url_builder);
|
||||
let res = templates.render_swagger(&ctx)?;
|
||||
Ok(Html(res))
|
||||
}
|
||||
|
||||
async fn swagger_callback(
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
State(templates): State<Templates>,
|
||||
) -> Result<Html<String>, FancyError> {
|
||||
let ctx = ApiDocContext::from_url_builder(&url_builder);
|
||||
let res = templates.render_swagger_callback(&ctx)?;
|
||||
Ok(Html(res))
|
||||
}
|
||||
|
@ -23,6 +23,9 @@
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use aide::openapi::{Server, ServerVariable};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
/// This is a dummy state, it should never be used.
|
||||
///
|
||||
/// We use it to generate the API schema, which doesn't execute any request.
|
||||
@ -58,10 +61,25 @@ macro_rules! impl_from_ref {
|
||||
impl_from_request_parts!(mas_storage::BoxRepository);
|
||||
impl_from_request_parts!(mas_storage::BoxClock);
|
||||
impl_from_request_parts!(mas_handlers::BoundActivityTracker);
|
||||
impl_from_ref!(mas_keystore::Keystore);
|
||||
impl_from_ref!(mas_router::UrlBuilder);
|
||||
impl_from_ref!(mas_templates::Templates);
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (api, _) = mas_handlers::admin_api_router::<DummyState>();
|
||||
let (mut api, _) = mas_handlers::admin_api_router::<DummyState>();
|
||||
|
||||
// Set the server list to a configurable base URL
|
||||
api.servers = vec![Server {
|
||||
url: "{base}".to_owned(),
|
||||
variables: IndexMap::from([(
|
||||
"base".to_owned(),
|
||||
ServerVariable {
|
||||
default: "/".to_owned(),
|
||||
..ServerVariable::default()
|
||||
},
|
||||
)]),
|
||||
..Server::default()
|
||||
}];
|
||||
|
||||
let mut stdout = std::io::stdout();
|
||||
serde_json::to_writer_pretty(&mut stdout, &api)?;
|
||||
|
||||
|
@ -870,3 +870,24 @@ pub struct GraphQLPlayground;
|
||||
impl SimpleRoute for GraphQLPlayground {
|
||||
const PATH: &'static str = "/graphql/playground";
|
||||
}
|
||||
|
||||
/// `GET /api/spec.json`
|
||||
pub struct ApiSpec;
|
||||
|
||||
impl SimpleRoute for ApiSpec {
|
||||
const PATH: &'static str = "/api/spec.json";
|
||||
}
|
||||
|
||||
/// `GET /api/doc/`
|
||||
pub struct ApiDoc;
|
||||
|
||||
impl SimpleRoute for ApiDoc {
|
||||
const PATH: &'static str = "/api/doc/";
|
||||
}
|
||||
|
||||
/// `GET /api/doc/oauth2-callback`
|
||||
pub struct ApiDocCallback;
|
||||
|
||||
impl SimpleRoute for ApiDocCallback {
|
||||
const PATH: &'static str = "/api/doc/oauth2-callback";
|
||||
}
|
||||
|
@ -28,7 +28,9 @@ pub struct UrlBuilder {
|
||||
}
|
||||
|
||||
impl UrlBuilder {
|
||||
fn absolute_url_for<U>(&self, destination: &U) -> Url
|
||||
/// Create an absolute URL for a route
|
||||
#[must_use]
|
||||
pub fn absolute_url_for<U>(&self, destination: &U) -> Url
|
||||
where
|
||||
U: Route,
|
||||
{
|
||||
|
@ -337,6 +337,35 @@ impl TemplateContext for AppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Context used by the `swagger/doc.html` template
|
||||
#[derive(Serialize)]
|
||||
pub struct ApiDocContext {
|
||||
openapi_url: Url,
|
||||
callback_url: Url,
|
||||
}
|
||||
|
||||
impl ApiDocContext {
|
||||
/// Constructs a context for the API documentation page giben the
|
||||
/// [`UrlBuilder`]
|
||||
#[must_use]
|
||||
pub fn from_url_builder(url_builder: &UrlBuilder) -> Self {
|
||||
Self {
|
||||
openapi_url: url_builder.absolute_url_for(&mas_router::ApiSpec),
|
||||
callback_url: url_builder.absolute_url_for(&mas_router::ApiDocCallback),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TemplateContext for ApiDocContext {
|
||||
fn sample(_now: chrono::DateTime<Utc>, _rng: &mut impl Rng) -> Vec<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let url_builder = UrlBuilder::new("https://example.com/".parse().unwrap(), None, None);
|
||||
vec![Self::from_url_builder(&url_builder)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields of the login form
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
@ -42,16 +42,16 @@ mod macros;
|
||||
|
||||
pub use self::{
|
||||
context::{
|
||||
AppContext, CompatSsoContext, ConsentContext, DeviceConsentContext, DeviceLinkContext,
|
||||
DeviceLinkFormField, EmailAddContext, EmailRecoveryContext, EmailVerificationContext,
|
||||
EmailVerificationPageContext, EmptyContext, ErrorContext, FormPostContext, IndexContext,
|
||||
LoginContext, LoginFormField, NotFoundContext, PolicyViolationContext, PostAuthContext,
|
||||
PostAuthContextInner, ReauthContext, ReauthFormField, RecoveryExpiredContext,
|
||||
RecoveryFinishContext, RecoveryFinishFormField, RecoveryProgressContext,
|
||||
RecoveryStartContext, RecoveryStartFormField, RegisterContext, RegisterFormField,
|
||||
SiteBranding, SiteConfigExt, SiteFeatures, TemplateContext, UpstreamExistingLinkContext,
|
||||
UpstreamRegister, UpstreamRegisterFormField, UpstreamSuggestLink, WithCaptcha, WithCsrf,
|
||||
WithLanguage, WithOptionalSession, WithSession,
|
||||
ApiDocContext, AppContext, CompatSsoContext, ConsentContext, DeviceConsentContext,
|
||||
DeviceLinkContext, DeviceLinkFormField, EmailAddContext, EmailRecoveryContext,
|
||||
EmailVerificationContext, EmailVerificationPageContext, EmptyContext, ErrorContext,
|
||||
FormPostContext, IndexContext, LoginContext, LoginFormField, NotFoundContext,
|
||||
PolicyViolationContext, PostAuthContext, PostAuthContextInner, ReauthContext,
|
||||
ReauthFormField, RecoveryExpiredContext, RecoveryFinishContext, RecoveryFinishFormField,
|
||||
RecoveryProgressContext, RecoveryStartContext, RecoveryStartFormField, RegisterContext,
|
||||
RegisterFormField, SiteBranding, SiteConfigExt, SiteFeatures, TemplateContext,
|
||||
UpstreamExistingLinkContext, UpstreamRegister, UpstreamRegisterFormField,
|
||||
UpstreamSuggestLink, WithCaptcha, WithCsrf, WithLanguage, WithOptionalSession, WithSession,
|
||||
},
|
||||
forms::{FieldError, FormError, FormField, FormState, ToFormState},
|
||||
};
|
||||
@ -324,6 +324,12 @@ register_templates! {
|
||||
/// Render the frontend app
|
||||
pub fn render_app(WithLanguage<AppContext>) { "app.html" }
|
||||
|
||||
/// Render the Swagger API reference
|
||||
pub fn render_swagger(ApiDocContext) { "swagger/doc.html" }
|
||||
|
||||
/// Render the Swagger OAuth2 callback page
|
||||
pub fn render_swagger_callback(ApiDocContext) { "swagger/oauth2-redirect.html" }
|
||||
|
||||
/// Render the login page
|
||||
pub fn render_login(WithLanguage<WithCsrf<LoginContext>>) { "pages/login.html" }
|
||||
|
||||
@ -423,6 +429,8 @@ impl Templates {
|
||||
) -> anyhow::Result<()> {
|
||||
check::render_not_found(self, now, rng)?;
|
||||
check::render_app(self, now, rng)?;
|
||||
check::render_swagger(self, now, rng)?;
|
||||
check::render_swagger_callback(self, now, rng)?;
|
||||
check::render_login(self, now, rng)?;
|
||||
check::render_register(self, now, rng)?;
|
||||
check::render_consent(self, now, rng)?;
|
||||
|
24
docs/api/index.html
Normal file
24
docs/api/index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="SwaggerUI" />
|
||||
<title>API documentation</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js" crossorigin></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: './spec.json',
|
||||
dom_id: '#swagger-ui',
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
],
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
80
docs/api/oauth2-redirect.html
Normal file
80
docs/api/oauth2-redirect.html
Normal file
@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- This is taken from the swagger-ui/dist/oauth2-redirect.html file -->
|
||||
<head>
|
||||
<title>API documentation: OAuth2 Redirect</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
function run () {
|
||||
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||
var sentState = oauth2.state;
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
var isValid, qp, arr;
|
||||
|
||||
if (/code|token|error/.test(window.location.hash)) {
|
||||
qp = window.location.hash.substring(1).replace('?', '&');
|
||||
} else {
|
||||
qp = location.search.substring(1);
|
||||
}
|
||||
|
||||
arr = qp.split("&");
|
||||
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
|
||||
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||
function (key, value) {
|
||||
return key === "" ? value : decodeURIComponent(value);
|
||||
}
|
||||
) : {};
|
||||
|
||||
isValid = qp.state === sentState;
|
||||
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorization_code"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "warning",
|
||||
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
||||
});
|
||||
}
|
||||
|
||||
if (qp.code) {
|
||||
delete oauth2.state;
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg;
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
||||
});
|
||||
}
|
||||
} else {
|
||||
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
run();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
run();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
2365
frontend/package-lock.json
generated
2365
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,7 @@
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^14.1.2",
|
||||
"swagger-ui-react": "^5.17.14",
|
||||
"urql": "^4.1.0",
|
||||
"vaul": "^0.9.1",
|
||||
"zod": "^3.23.8"
|
||||
@ -60,6 +61,7 @@
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-test-renderer": "^18.3.0",
|
||||
"@types/swagger-ui-react": "^4.18.3",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"@vitest/coverage-v8": "^1.6.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
|
35
frontend/src/swagger.tsx
Normal file
35
frontend/src/swagger.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { createRoot } from "react-dom/client";
|
||||
import SwaggerUI from "swagger-ui-react";
|
||||
import "swagger-ui-react/swagger-ui.css";
|
||||
|
||||
type ApiConfig = {
|
||||
openapiUrl: string;
|
||||
callbackUrl: string;
|
||||
};
|
||||
|
||||
interface IWindow {
|
||||
API_CONFIG?: ApiConfig;
|
||||
}
|
||||
|
||||
const config = typeof window !== "undefined" && (window as IWindow).API_CONFIG;
|
||||
if (!config) {
|
||||
throw new Error("API_CONFIG is not defined");
|
||||
}
|
||||
|
||||
createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<SwaggerUI url={config.openapiUrl} oauth2RedirectUrl={config.callbackUrl} />,
|
||||
);
|
@ -65,6 +65,7 @@ export default defineConfig((env) => ({
|
||||
resolve(__dirname, "src/main.tsx"),
|
||||
resolve(__dirname, "src/shared.css"),
|
||||
resolve(__dirname, "src/templates.css"),
|
||||
resolve(__dirname, "src/swagger.tsx"),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -5,7 +5,7 @@ set -eu
|
||||
export SQLX_OFFLINE=1
|
||||
BASE_DIR="$(dirname "$0")/.."
|
||||
CONFIG_SCHEMA="${BASE_DIR}/docs/config.schema.json"
|
||||
API_SCHEMA="${BASE_DIR}/docs/api.schema.json"
|
||||
API_SCHEMA="${BASE_DIR}/docs/api/spec.json"
|
||||
GRAPHQL_SCHEMA="${BASE_DIR}/frontend/schema.graphql"
|
||||
POLICIES_SCHEMA="${BASE_DIR}/policies/schema/"
|
||||
|
||||
|
35
templates/swagger/doc.html
Normal file
35
templates/swagger/doc.html
Normal file
@ -0,0 +1,35 @@
|
||||
{#
|
||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>API documentation</title>
|
||||
<script>
|
||||
window.API_CONFIG = {
|
||||
openapiUrl: "{{ openapi_url | add_slashes | safe }}",
|
||||
callbackUrl: "{{ callback_url | add_slashes | safe }}",
|
||||
};
|
||||
</script>
|
||||
{{ include_asset('src/swagger.tsx') | indent(4) | safe }}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
97
templates/swagger/oauth2-redirect.html
Normal file
97
templates/swagger/oauth2-redirect.html
Normal file
@ -0,0 +1,97 @@
|
||||
{#
|
||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{# This is taken from the swagger-ui/dist/oauth2-redirect.html file #}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>API documentation: OAuth2 Redirect</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
function run () {
|
||||
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||
var sentState = oauth2.state;
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
var isValid, qp, arr;
|
||||
|
||||
if (/code|token|error/.test(window.location.hash)) {
|
||||
qp = window.location.hash.substring(1).replace('?', '&');
|
||||
} else {
|
||||
qp = location.search.substring(1);
|
||||
}
|
||||
|
||||
arr = qp.split("&");
|
||||
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
|
||||
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||
function (key, value) {
|
||||
return key === "" ? value : decodeURIComponent(value);
|
||||
}
|
||||
) : {};
|
||||
|
||||
isValid = qp.state === sentState;
|
||||
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorization_code"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "warning",
|
||||
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
||||
});
|
||||
}
|
||||
|
||||
if (qp.code) {
|
||||
delete oauth2.state;
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg;
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
||||
});
|
||||
}
|
||||
} else {
|
||||
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
run();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
run();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user