mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-03 20:50:19 +08:00
feat: add Oauth 2.0 Token Introspection(rfc7662) endpoint support (#532)
Signed-off-by: Leon <leondevlifelog@gmail.com>
This commit is contained in:
@ -229,3 +229,65 @@ func (c *ApiController) TokenLogout() {
|
|||||||
c.Data["json"] = wrapActionResponse(flag)
|
c.Data["json"] = wrapActionResponse(flag)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntrospectToken
|
||||||
|
// @Title IntrospectToken
|
||||||
|
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a
|
||||||
|
// parameter representing an OAuth 2.0 token and returns a JSON document
|
||||||
|
// representing the meta information surrounding the
|
||||||
|
// token, including whether this token is currently active.
|
||||||
|
// This endpoint only support Basic Authorization.
|
||||||
|
// @Param body body {object.TokenIntrospectionRequest} true "the request body"
|
||||||
|
// @Success 200 {object} object.IntrospectionResponse The Response object
|
||||||
|
// @router /login/oauth/introspect [post]
|
||||||
|
func (c *ApiController) IntrospectToken() {
|
||||||
|
var body object.TokenIntrospectionRequest
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &body)
|
||||||
|
clientId, clientSecret, ok := c.Ctx.Request.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
util.LogWarning(c.Ctx, "Basic Authorization parses failed")
|
||||||
|
c.Data["json"] = Response{Status: "error", Msg: "Unauthorized operation"}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
application := object.GetApplicationByClientId(clientId)
|
||||||
|
if application == nil || application.ClientSecret != clientSecret {
|
||||||
|
util.LogWarning(c.Ctx, "Basic Authorization failed")
|
||||||
|
c.Data["json"] = Response{Status: "error", Msg: "Unauthorized operation"}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token := object.GetTokenByTokenAndApplication(body.Token, application.Name)
|
||||||
|
if token == nil {
|
||||||
|
util.LogWarning(c.Ctx, "application: %s can not find token", application.Name)
|
||||||
|
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jwtToken, err := object.ParseJwtTokenByApplication(body.Token, application)
|
||||||
|
if err != nil || jwtToken.Valid() != nil {
|
||||||
|
// and token revoked case. but we not implement
|
||||||
|
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
|
||||||
|
// refs: https://tools.ietf.org/html/rfc7009
|
||||||
|
util.LogWarning(c.Ctx, "token invalid")
|
||||||
|
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = &object.IntrospectionResponse{
|
||||||
|
Active: true,
|
||||||
|
Scope: jwtToken.Scope,
|
||||||
|
ClientId: clientId,
|
||||||
|
Username: token.User,
|
||||||
|
TokenType: token.TokenType,
|
||||||
|
Exp: jwtToken.ExpiresAt.Unix(),
|
||||||
|
Iat: jwtToken.IssuedAt.Unix(),
|
||||||
|
Nbf: jwtToken.NotBefore.Unix(),
|
||||||
|
Sub: jwtToken.Subject,
|
||||||
|
Aud: jwtToken.Audience,
|
||||||
|
Iss: jwtToken.Issuer,
|
||||||
|
Jti: jwtToken.Id,
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
@ -60,6 +60,29 @@ type TokenWrapper struct {
|
|||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TokenIntrospectionRequest struct {
|
||||||
|
// access_token's value or refresh_token's value
|
||||||
|
Token string `json:"token"`
|
||||||
|
// pass this parameter to help the authorization server optimize the token lookup.
|
||||||
|
// value is one of `access_token` or `refresh_token`
|
||||||
|
TokenTypeHint string `json:"token_type_hint,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntrospectionResponse struct {
|
||||||
|
Active bool `json:"active"`
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
|
ClientId string `json:"client_id,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
TokenType string `json:"token_type,omitempty"`
|
||||||
|
Exp int64 `json:"exp,omitempty"`
|
||||||
|
Iat int64 `json:"iat,omitempty"`
|
||||||
|
Nbf int64 `json:"nbf,omitempty"`
|
||||||
|
Sub string `json:"sub,omitempty"`
|
||||||
|
Aud []string `json:"aud,omitempty"`
|
||||||
|
Iss string `json:"iss,omitempty"`
|
||||||
|
Jti string `json:"jti,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func GetTokenCount(owner, field, value string) int {
|
func GetTokenCount(owner, field, value string) int {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
count, err := session.Count(&Token{})
|
count, err := session.Count(&Token{})
|
||||||
@ -198,6 +221,15 @@ func GetTokenByAccessToken(accessToken string) *Token {
|
|||||||
return &token
|
return &token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTokenByTokenAndApplication(token string, application string) *Token {
|
||||||
|
tokenResult := Token{}
|
||||||
|
existed, err := adapter.Engine.Where("(refresh_token = ? or access_token = ? ) and application = ?", token, token, application).Get(&tokenResult)
|
||||||
|
if err != nil || !existed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &tokenResult
|
||||||
|
}
|
||||||
|
|
||||||
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" && responseType != "token" && responseType != "id_token" {
|
if responseType != "code" && responseType != "token" && responseType != "id_token" {
|
||||||
return fmt.Sprintf("error: grant_type: %s is not supported in this application", responseType), nil
|
return fmt.Sprintf("error: grant_type: %s is not supported in this application", responseType), nil
|
||||||
|
@ -147,3 +147,7 @@ func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
|
|||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseJwtTokenByApplication(token string, application *Application) (*Claims, error) {
|
||||||
|
return ParseJwtToken(token, getCertByApplication(application))
|
||||||
|
}
|
||||||
|
@ -127,6 +127,7 @@ func initAPI() {
|
|||||||
beego.Router("/api/login/oauth/code", &controllers.ApiController{}, "POST:GetOAuthCode")
|
beego.Router("/api/login/oauth/code", &controllers.ApiController{}, "POST:GetOAuthCode")
|
||||||
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
|
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
|
||||||
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
|
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
|
||||||
|
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
|
||||||
beego.Router("/api/login/oauth/logout", &controllers.ApiController{}, "GET:TokenLogout")
|
beego.Router("/api/login/oauth/logout", &controllers.ApiController{}, "GET:TokenLogout")
|
||||||
|
|
||||||
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
|
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
|
||||||
|
Reference in New Issue
Block a user