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:
Steve0x2a
2022-03-01 19:09:59 +08:00
committed by GitHub
parent d48d515c36
commit 697b3e4998
6 changed files with 70 additions and 10 deletions

View File

@ -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 {

View File

@ -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)}
} }

View File

@ -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
}

View File

@ -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>

View File

@ -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);

View File

@ -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}`);