mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-16 05:43:50 +08:00
feat: add implicit flow support (#520)
* feat: add implicit flow support Signed-off-by: Steve0x2a <stevesough@gmail.com> * fix: idp support in implicit flow Signed-off-by: Steve0x2a <stevesough@gmail.com>
This commit is contained in:
@ -24,8 +24,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ResponseTypeLogin = "login"
|
ResponseTypeLogin = "login"
|
||||||
ResponseTypeCode = "code"
|
ResponseTypeCode = "code"
|
||||||
|
ResponseTypeToken = "token"
|
||||||
|
ResponseTypeIdToken = "id_token"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RequestForm struct {
|
type RequestForm struct {
|
||||||
|
@ -38,6 +38,14 @@ func codeToResponse(code *object.Code) *Response {
|
|||||||
return &Response{Status: "ok", Msg: "", Data: code.Code}
|
return &Response{Status: "ok", Msg: "", Data: code.Code}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tokenToResponse(token *object.Token) *Response {
|
||||||
|
if token.AccessToken == "" {
|
||||||
|
return &Response{Status: "error", Msg: "fail to get accessToken", Data: token.AccessToken}
|
||||||
|
}
|
||||||
|
return &Response{Status: "ok", Msg: "", Data: token.AccessToken}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// HandleLoggedIn ...
|
// HandleLoggedIn ...
|
||||||
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
|
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
|
||||||
userId := user.GetId()
|
userId := user.GetId()
|
||||||
@ -66,6 +74,15 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
|||||||
// The prompt page needs the user to be signed in
|
// The prompt page needs the user to be signed in
|
||||||
c.SetSessionUsername(userId)
|
c.SetSessionUsername(userId)
|
||||||
}
|
}
|
||||||
|
} else if form.Type == ResponseTypeToken || form.Type == ResponseTypeIdToken { //implicit flow
|
||||||
|
if !object.IsGrantTypeValid(form.Type, application.GrantTypes) {
|
||||||
|
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
|
||||||
|
} else {
|
||||||
|
scope := c.Input().Get("scope")
|
||||||
|
token, _ := object.GetTokenByUser(application, user, scope, c.Ctx.Request.Host)
|
||||||
|
resp = tokenToResponse(token)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
resp = &Response{Status: "error", Msg: fmt.Sprintf("Unknown response type: %s", form.Type)}
|
resp = &Response{Status: "error", Msg: fmt.Sprintf("Unknown response type: %s", form.Type)}
|
||||||
}
|
}
|
||||||
|
@ -180,8 +180,8 @@ func GetTokenByAccessToken(accessToken string) *Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string) (string, *Application) {
|
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string) (string, *Application) {
|
||||||
if responseType != "code" {
|
if responseType != "code" && responseType != "token" && responseType != "id_token" {
|
||||||
return "response_type should be \"code\"", nil
|
return fmt.Sprintf("error: grant_type: %s is not supported in this application", responseType), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
application := GetApplicationByClientId(clientId)
|
application := GetApplicationByClientId(clientId)
|
||||||
@ -274,7 +274,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Check if grantType is allowed in the current application
|
//Check if grantType is allowed in the current application
|
||||||
if !isGrantTypeValid(grantType, application.GrantTypes) {
|
if !IsGrantTypeValid(grantType, application.GrantTypes) {
|
||||||
return &TokenWrapper{
|
return &TokenWrapper{
|
||||||
AccessToken: fmt.Sprintf("error: grant_type: %s is not supported in this application", grantType),
|
AccessToken: fmt.Sprintf("error: grant_type: %s is not supported in this application", grantType),
|
||||||
TokenType: "",
|
TokenType: "",
|
||||||
@ -418,7 +418,7 @@ func pkceChallenge(verifier string) string {
|
|||||||
|
|
||||||
// Check if grantType is allowed in the current application
|
// Check if grantType is allowed in the current application
|
||||||
// authorization_code is allowed by default
|
// authorization_code is allowed by default
|
||||||
func isGrantTypeValid(method string, grantTypes []string) bool {
|
func IsGrantTypeValid(method string, grantTypes []string) bool {
|
||||||
if method == "authorization_code" {
|
if method == "authorization_code" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -527,3 +527,28 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
|||||||
AddToken(token)
|
AddToken(token)
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implicit flow
|
||||||
|
func GetTokenByUser(application *Application, user *User, scope string, host string) (*Token, error) {
|
||||||
|
accessToken, refreshToken, err := generateJwtToken(application, user, "", scope, host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
token := &Token{
|
||||||
|
Owner: application.Owner,
|
||||||
|
Name: util.GenerateId(),
|
||||||
|
CreatedTime: util.GetCurrentTime(),
|
||||||
|
Application: application.Name,
|
||||||
|
Organization: user.Owner,
|
||||||
|
User: user.Name,
|
||||||
|
Code: util.GenerateClientId(),
|
||||||
|
AccessToken: accessToken,
|
||||||
|
RefreshToken: refreshToken,
|
||||||
|
ExpiresIn: application.ExpireInHours * 60,
|
||||||
|
Scope: scope,
|
||||||
|
TokenType: "Bearer",
|
||||||
|
CodeIsUsed: true,
|
||||||
|
}
|
||||||
|
AddToken(token)
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
@ -453,6 +453,8 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{id: "authorization_code", name: "Authorization Code"},
|
{id: "authorization_code", name: "Authorization Code"},
|
||||||
{id: "password", name: "Password"},
|
{id: "password", name: "Password"},
|
||||||
{id: "client_credentials", name: "Client Credentials"},
|
{id: "client_credentials", name: "Client Credentials"},
|
||||||
|
{id: "token", name: "Token"},
|
||||||
|
{id: "id_token",name:"ID Token"},
|
||||||
].map((item, index)=><Option key={index} value={item.id}>{item.name}</Option>)
|
].map((item, index)=><Option key={index} value={item.id}>{item.name}</Option>)
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
|
@ -58,6 +58,10 @@ class AuthCallback extends React.Component {
|
|||||||
if (authServerUrl === realRedirectUrl) {
|
if (authServerUrl === realRedirectUrl) {
|
||||||
return "login";
|
return "login";
|
||||||
} else {
|
} else {
|
||||||
|
const responseType = innerParams.get("response_type");
|
||||||
|
if (responseType !== null) {
|
||||||
|
return responseType
|
||||||
|
}
|
||||||
return "code";
|
return "code";
|
||||||
}
|
}
|
||||||
} else if (method === "link") {
|
} else if (method === "link") {
|
||||||
@ -116,6 +120,9 @@ class AuthCallback extends React.Component {
|
|||||||
const code = res.data;
|
const code = res.data;
|
||||||
Setting.goToLink(`${oAuthParams.redirectUri}?code=${code}&state=${oAuthParams.state}`);
|
Setting.goToLink(`${oAuthParams.redirectUri}?code=${code}&state=${oAuthParams.state}`);
|
||||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
||||||
|
} else if (responseType === "token" || responseType === "id_token"){
|
||||||
|
const token = res.data;
|
||||||
|
Setting.goToLink(`${oAuthParams.redirectUri}?${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
|
||||||
} else if (responseType === "link") {
|
} else if (responseType === "link") {
|
||||||
const from = innerParams.get("from");
|
const from = innerParams.get("from");
|
||||||
Setting.goToLinkSoft(this, from);
|
Setting.goToLinkSoft(this, from);
|
||||||
|
@ -116,14 +116,18 @@ class LoginPage extends React.Component {
|
|||||||
onFinish(values) {
|
onFinish(values) {
|
||||||
const application = this.getApplicationObj();
|
const application = this.getApplicationObj();
|
||||||
const ths = this;
|
const ths = this;
|
||||||
values["type"] = this.state.type;
|
|
||||||
values["phonePrefix"] = this.getApplicationObj()?.organizationObj.phonePrefix;
|
|
||||||
const oAuthParams = Util.getOAuthGetParameters();
|
const oAuthParams = Util.getOAuthGetParameters();
|
||||||
|
if (oAuthParams !== null && oAuthParams.responseType!= null && oAuthParams.responseType !== "") {
|
||||||
|
values["type"] = oAuthParams.responseType
|
||||||
|
}else{
|
||||||
|
values["type"] = this.state.type;
|
||||||
|
}
|
||||||
|
values["phonePrefix"] = this.getApplicationObj()?.organizationObj.phonePrefix;
|
||||||
|
|
||||||
AuthBackend.login(values, oAuthParams)
|
AuthBackend.login(values, oAuthParams)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 'ok') {
|
if (res.status === 'ok') {
|
||||||
const responseType = this.state.type;
|
const responseType = values["type"];
|
||||||
if (responseType === "login") {
|
if (responseType === "login") {
|
||||||
Util.showMessage("success", `Logged in successfully`);
|
Util.showMessage("success", `Logged in successfully`);
|
||||||
|
|
||||||
@ -156,6 +160,9 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
||||||
|
} else if (responseType === "token" || responseType === "id_token") {
|
||||||
|
const accessToken = res.data;
|
||||||
|
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||||
|
Reference in New Issue
Block a user