mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-02 11:20:18 +08:00
feat: support third-party application to login with SAML rather than only Casdoor itself (#350)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
This commit is contained in:
@ -15,5 +15,4 @@ verificationCodeTimeout = 10
|
|||||||
initScore = 2000
|
initScore = 2000
|
||||||
logPostOnly = true
|
logPostOnly = true
|
||||||
oidcOrigin = "https://door.casbin.com"
|
oidcOrigin = "https://door.casbin.com"
|
||||||
samlOrigin = "http://localhost:8000"
|
samlOrigin = "http://localhost:8000"
|
||||||
samlRequestOrigin = "http://localhost:7001"
|
|
@ -15,6 +15,7 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -369,8 +370,14 @@ func (c *ApiController) GetSamlLogin() {
|
|||||||
func (c *ApiController) HandleSamlLogin() {
|
func (c *ApiController) HandleSamlLogin() {
|
||||||
relayState := c.Input().Get("RelayState")
|
relayState := c.Input().Get("RelayState")
|
||||||
samlResponse := c.Input().Get("SAMLResponse")
|
samlResponse := c.Input().Get("SAMLResponse")
|
||||||
|
decode, err := base64.StdEncoding.DecodeString(relayState)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
}
|
||||||
|
slice := strings.Split(string(decode), "&")
|
||||||
|
relayState = url.QueryEscape(relayState)
|
||||||
samlResponse = url.QueryEscape(samlResponse)
|
samlResponse = url.QueryEscape(samlResponse)
|
||||||
targetUrl := fmt.Sprintf("%s/callback/saml?replayState=%s&samlResponse=%s",
|
targetUrl := fmt.Sprintf("%s?relayState=%s&samlResponse=%s",
|
||||||
beego.AppConfig.String("samlRequestOrigin"), relayState, samlResponse)
|
slice[4], relayState, samlResponse)
|
||||||
c.Redirect(targetUrl, 303)
|
c.Redirect(targetUrl, 303)
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ export function getSamlLogin(providerId) {
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginWithSaml(values) {
|
export function loginWithSaml(values, param) {
|
||||||
return fetch(`${authConfig.serverUrl}/api/login`, {
|
return fetch(`${authConfig.serverUrl}/api/login${param}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
body: JSON.stringify(values),
|
body: JSON.stringify(values),
|
||||||
|
@ -186,8 +186,15 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSamlUrl(providerId) {
|
getSamlUrl(providerId) {
|
||||||
|
const params = new URLSearchParams(this.props.location.search);
|
||||||
|
let clientId = params.get("client_id")
|
||||||
|
let application = params.get("state");
|
||||||
|
let realRedirectUri = params.get("redirect_uri");
|
||||||
|
let redirectUri = `${window.location.origin}/callback/saml`
|
||||||
|
let providerName = providerId.split('/')[1];
|
||||||
AuthBackend.getSamlLogin(providerId).then((res) => {
|
AuthBackend.getSamlLogin(providerId).then((res) => {
|
||||||
window.location.href = res.data
|
const replyState = `${clientId}&${application}&${providerName}&${realRedirectUri}&${redirectUri}`;
|
||||||
|
window.location.href = `${res.data}&RelayState=${btoa(replyState)}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import * as AuthBackend from "./AuthBackend";
|
|||||||
import * as Util from "./Util";
|
import * as Util from "./Util";
|
||||||
import * as Setting from "../Setting";
|
import * as Setting from "../Setting";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import {authConfig} from "./Auth";
|
||||||
|
|
||||||
class SamlCallback extends React.Component {
|
class SamlCallback extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -29,28 +30,61 @@ class SamlCallback extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getResponseType(redirectUri) {
|
||||||
|
const authServerUrl = authConfig.serverUrl;
|
||||||
|
// Casdoor's own login page, so "code" is not necessary
|
||||||
|
if (redirectUri === "null") {
|
||||||
|
return "login";
|
||||||
|
}
|
||||||
|
const realRedirectUrl = new URL(redirectUri).origin;
|
||||||
|
// For Casdoor itself, we use "login" directly
|
||||||
|
if (authServerUrl === realRedirectUrl) {
|
||||||
|
return "login";
|
||||||
|
} else {
|
||||||
|
return "code";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
const params = new URLSearchParams(this.props.location.search);
|
const params = new URLSearchParams(this.props.location.search);
|
||||||
let relayState = params.get('relayState')
|
let relayState = params.get('relayState')
|
||||||
let samlResponse = params.get('samlResponse')
|
let samlResponse = params.get('samlResponse')
|
||||||
let redirectUri = `${window.location.origin}/callback`;
|
const messages = atob(relayState).split('&');
|
||||||
const applicationName = "app-built-in"
|
const clientId = messages[0];
|
||||||
|
const applicationName = messages[1] === "null" ? "app-built-in" : messages[1];
|
||||||
|
const providerName = messages[2];
|
||||||
|
const redirectUri = messages[3];
|
||||||
|
const responseType = this.getResponseType(redirectUri);
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
type: "login",
|
type: responseType,
|
||||||
application: applicationName,
|
application: applicationName,
|
||||||
provider: "aliyun-idaas",
|
provider: providerName,
|
||||||
state: applicationName,
|
state: applicationName,
|
||||||
redirectUri: redirectUri,
|
redirectUri: `${window.location.origin}/callback`,
|
||||||
method: "signup",
|
method: "signup",
|
||||||
relayState: relayState,
|
relayState: relayState,
|
||||||
samlResponse: encodeURIComponent(samlResponse),
|
samlResponse: encodeURIComponent(samlResponse),
|
||||||
};
|
};
|
||||||
AuthBackend.loginWithSaml(body)
|
|
||||||
|
let param;
|
||||||
|
if (clientId === null || clientId === "") {
|
||||||
|
param = ""
|
||||||
|
} else {
|
||||||
|
param = `?clientId=${clientId}&responseType=${responseType}&redirectUri=${redirectUri}&scope=read&state=${applicationName}`
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthBackend.loginWithSaml(body, param)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 'ok') {
|
if (res.status === 'ok') {
|
||||||
Util.showMessage("success", `Logged in successfully`);
|
const responseType = this.getResponseType(redirectUri);
|
||||||
// Setting.goToLinkSoft(this, "/");
|
if (responseType === "login") {
|
||||||
Setting.goToLink("/");
|
Util.showMessage("success", `Logged in successfully`);
|
||||||
|
Setting.goToLink("/");
|
||||||
|
} else if (responseType === "code") {
|
||||||
|
const code = res.data;
|
||||||
|
Setting.goToLink(`${redirectUri}?code=${code}&state=${applicationName}`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
|
Reference in New Issue
Block a user