Implement client_credentials g.type with user imp.
This commit is contained in:
+4
-5
@@ -81,13 +81,12 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
handlers.GetLinks().OAuth2AuthorizeClient,
|
||||
))
|
||||
|
||||
oauth2Server.SetClientAuthorizedHandler(func(id string, grant oauth2def.GrantType) (bool, error) {
|
||||
oauth2Server.SetClientAuthorizedHandler(func(id string, grant oauth2def.GrantType) (allowed bool, err error) {
|
||||
// this is a bit silly and a bad design of the oauth2 server lib
|
||||
// why do we need to keep on load the client??
|
||||
var (
|
||||
clientID uint64
|
||||
client *types.AuthClient
|
||||
err error
|
||||
)
|
||||
|
||||
clientID, err = strconv.ParseUint(id, 10, 64)
|
||||
@@ -108,7 +107,7 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
return true, nil
|
||||
})
|
||||
|
||||
oauth2Server.SetClientScopeHandler(func(id, ss string) (allowed bool, err error) {
|
||||
oauth2Server.SetClientScopeHandler(func(tgr *oauth2def.TokenGenerateRequest) (allowed bool, err error) {
|
||||
// this is a bit silly and a bad design of the oauth2 server lib
|
||||
// why do we need to keep on load the client??
|
||||
var (
|
||||
@@ -116,7 +115,7 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
client *types.AuthClient
|
||||
)
|
||||
|
||||
clientID, err = strconv.ParseUint(id, 10, 64)
|
||||
clientID, err = strconv.ParseUint(tgr.ClientID, 10, 64)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not authorize client: %w", err)
|
||||
}
|
||||
@@ -127,7 +126,7 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
}
|
||||
|
||||
// ensure all requested scopes are allowed on a client
|
||||
for _, scope := range strings.Split(ss, " ") {
|
||||
for _, scope := range strings.Split(tgr.Scope, " ") {
|
||||
if !auth.CheckScope(client.Scope, scope) {
|
||||
return false, fmt.Errorf("client does not allow use of '%s' scope", scope)
|
||||
}
|
||||
|
||||
@@ -160,22 +160,10 @@ func (h AuthHandlers) oauth2Token(req *request.AuthReq) (err error) {
|
||||
client, err := h.loadRequestedClient(req)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
return h.tokenError(req.Response, err)
|
||||
}
|
||||
|
||||
if err = client.Verify(); err != nil {
|
||||
return fmt.Errorf("invalid client: %w", err)
|
||||
} else {
|
||||
// add client to context so we can reach it from client store via context.Value() fn
|
||||
//
|
||||
// this way we work around the limitations we have with the oauth2 lib.
|
||||
r := req.Request.Clone(context.WithValue(req.Context(), &oauth2.ContextClientStore{}, client))
|
||||
|
||||
// handle token request with extended context that now holds client!
|
||||
err = h.OAuth2.HandleTokenRequest(req.Response, r)
|
||||
}
|
||||
|
||||
return
|
||||
return h.handleTokenRequest(req, client)
|
||||
}
|
||||
|
||||
func (h AuthHandlers) oauth2Info(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -289,8 +277,9 @@ func (h AuthHandlers) oauth2authorizeDefaultClientProc(req *request.AuthReq) (er
|
||||
h.DefaultClient.Secret,
|
||||
)
|
||||
|
||||
req.Status = -1
|
||||
return h.OAuth2.HandleTokenRequest(req.Response, r)
|
||||
req.Request = r
|
||||
|
||||
return h.handleTokenRequest(req, h.DefaultClient)
|
||||
}
|
||||
|
||||
func (h AuthHandlers) verifyDefaultClient() error {
|
||||
@@ -356,6 +345,58 @@ func (h AuthHandlers) loadRequestedClient(req *request.AuthReq) (client *types.A
|
||||
}()
|
||||
}
|
||||
|
||||
func (h AuthHandlers) handleTokenRequest(req *request.AuthReq, client *types.AuthClient) error {
|
||||
req.Status = -1
|
||||
|
||||
var (
|
||||
r = req.Request
|
||||
w = req.Response
|
||||
ctx = req.Context()
|
||||
)
|
||||
|
||||
req.Status = -1
|
||||
|
||||
if err := client.Verify(); err != nil {
|
||||
return h.tokenError(w, fmt.Errorf("invalid client: %w", err))
|
||||
}
|
||||
|
||||
// add client to context so we can reach it from client store via context.Value() fn
|
||||
// this way we work around the limitations we have with the oauth2 lib.
|
||||
ctx = context.WithValue(ctx, &oauth2.ContextClientStore{}, client)
|
||||
r = req.Request.Clone(ctx)
|
||||
|
||||
gt, tgr, err := h.OAuth2.ValidationTokenRequest(r)
|
||||
if err != nil {
|
||||
return h.tokenError(w, err)
|
||||
}
|
||||
|
||||
if gt == oauth2def.ClientCredentials {
|
||||
// Authenticated with client credentials!
|
||||
//
|
||||
// We'll use info from client security
|
||||
if client.Security == nil || client.Security.ImpersonateUser == 0 {
|
||||
return h.tokenError(w, errors.Internal("auth client security configuration invalid"))
|
||||
}
|
||||
|
||||
tgr.UserID = strings.Join(append(
|
||||
[]string{fmt.Sprintf("%d", client.Security.ImpersonateUser)},
|
||||
client.Security.ForcedRoles...,
|
||||
), " ")
|
||||
}
|
||||
|
||||
ti, err := h.OAuth2.GetAccessToken(ctx, gt, tgr)
|
||||
if err != nil {
|
||||
return h.tokenError(w, err)
|
||||
}
|
||||
|
||||
return token(w, h.OAuth2.GetTokenData(ti), nil)
|
||||
}
|
||||
|
||||
func (h AuthHandlers) tokenError(w http.ResponseWriter, err error) error {
|
||||
data, statusCode, header := h.OAuth2.GetErrorData(err)
|
||||
return token(w, data, header, statusCode)
|
||||
}
|
||||
|
||||
func SubSplit(ti oauth2def.TokenInfo, data map[string]interface{}) {
|
||||
userIdWithRoles := strings.SplitN(ti.GetUserID(), " ", 2)
|
||||
data["sub"] = userIdWithRoles[0]
|
||||
@@ -395,3 +436,21 @@ func Profile(ctx context.Context, ti oauth2def.TokenInfo, data map[string]interf
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func token(w http.ResponseWriter, data map[string]interface{}, header http.Header, statusCode ...int) error {
|
||||
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
|
||||
for key := range header {
|
||||
w.Header().Set(key, header.Get(key))
|
||||
}
|
||||
|
||||
status := http.StatusOK
|
||||
if len(statusCode) > 0 && statusCode[0] > 0 {
|
||||
status = statusCode[0]
|
||||
}
|
||||
|
||||
w.WriteHeader(status)
|
||||
return json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (c CortezaTokenStore) Create(ctx context.Context, info oauth2.TokenInfo) (e
|
||||
if info.GetUserID() != "" {
|
||||
if oa2t.UserID = auth.ExtractUserIDFromSubClaim(info.GetUserID()); oa2t.UserID == 0 {
|
||||
// UserID stores collection of IDs: user's ID and set of all roles user is member of
|
||||
return fmt.Errorf("could not parse user ID from token info: %w", err)
|
||||
return fmt.Errorf("could not parse user ID from token info")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,8 +71,7 @@ func NewServer(manager *manage.Manager) *server.Server {
|
||||
oauth2.Refreshing,
|
||||
// before enabling ClientCredentials grant type, we need to know how to modify released token
|
||||
// using client's security info; how to enforce impersonated user and his roles.
|
||||
//
|
||||
// oauth2.ClientCredentials,
|
||||
oauth2.ClientCredentials,
|
||||
},
|
||||
AllowedCodeChallengeMethods: []oauth2.CodeChallengeMethod{
|
||||
oauth2.CodeChallengePlain,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
func NewUserAuthorizer(sm *request.SessionManager, loginURL, clientAuthURL string) server.UserAuthorizationHandler {
|
||||
return func(w http.ResponseWriter, r *http.Request) (identity string, err error) {
|
||||
|
||||
var (
|
||||
ses = sm.Get(r)
|
||||
au = request.GetAuthUser(ses)
|
||||
|
||||
@@ -28,7 +28,7 @@ require (
|
||||
github.com/go-chi/cors v1.0.0
|
||||
github.com/go-chi/httprate v0.4.0
|
||||
github.com/go-chi/jwtauth v0.0.0-20190109153619-47840abb19b3
|
||||
github.com/go-oauth2/oauth2/v4 v4.2.0
|
||||
github.com/go-oauth2/oauth2/v4 v4.3.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/golang/protobuf v1.4.2
|
||||
@@ -55,7 +55,6 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/client_golang v0.9.3
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
|
||||
github.com/spf13/afero v1.2.2
|
||||
|
||||
@@ -51,8 +51,6 @@ github.com/Masterminds/squirrel v1.1.1-0.20191017225151-12f2162c8d8d/go.mod h1:y
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PaesslerAG/gval v0.1.1/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
|
||||
github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
|
||||
github.com/PaesslerAG/gval v1.1.0 h1:k3RuxeZDO3eejD4cMPSt+74tUSvTnbGvLx0df4mdwFc=
|
||||
github.com/PaesslerAG/gval v1.1.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
|
||||
github.com/PaesslerAG/gval v1.1.1-0.20201104175134-7847ed0c7671 h1:mXmNWMJr5OPcaA00ryiN+FZphVa1EtUYiVNH8nfWALY=
|
||||
github.com/PaesslerAG/gval v1.1.1-0.20201104175134-7847ed0c7671/go.mod h1:Fa8gfkCmUsELXgayr8sfL/sw+VzCVoa03dcOcR/if2w=
|
||||
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
|
||||
@@ -136,6 +134,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-oauth2/oauth2/v4 v4.2.0 h1:wz9w9SwHrzC58XniFSGXIfl2R5Q6x1aecVuL/QBupl8=
|
||||
github.com/go-oauth2/oauth2/v4 v4.2.0/go.mod h1:+rsyi0o/ZbSfhL/3Xr/sAtL4brS+IdGj86PHVlPjE+4=
|
||||
github.com/go-oauth2/oauth2/v4 v4.3.0 h1:vp4goUmrq1YaPzpm34FDLlZiAkIqK3LsuNTTRyTnPbo=
|
||||
github.com/go-oauth2/oauth2/v4 v4.3.0/go.mod h1:+rsyi0o/ZbSfhL/3Xr/sAtL4brS+IdGj86PHVlPjE+4=
|
||||
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
@@ -320,8 +320,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
@@ -411,8 +409,6 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
||||
+22
-12
@@ -7,19 +7,29 @@ import (
|
||||
)
|
||||
|
||||
func MiddlewareValidOnly(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var ctx = r.Context()
|
||||
return AccessTokenCheck("api")(next)
|
||||
}
|
||||
|
||||
if !CheckScope(ctx.Value(scopeCtxKey{}), "api") {
|
||||
api.Send(w, r, errors.New("Unauthorized scope"))
|
||||
return
|
||||
}
|
||||
func AccessTokenCheck(scope ...string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var ctx = r.Context()
|
||||
|
||||
if !GetIdentityFromContext(ctx).Valid() {
|
||||
api.Send(w, r, errors.New("Unauthorized"))
|
||||
return
|
||||
}
|
||||
for _, s := range scope {
|
||||
if !CheckScope(ctx.Value(scopeCtxKey{}), s) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
api.Send(w, r, errors.New("unauthorized scope"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
if !GetIdentityFromContext(ctx).Valid() {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
api.Send(w, r, errors.New("unauthorized"))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// AuthClient - An organisation may have many authClients. AuthClients may have many channels available. Access to channels may be shared between authClients.
|
||||
AuthClient struct {
|
||||
ID uint64 `json:"authClientID,string"`
|
||||
|
||||
@@ -70,7 +69,7 @@ type (
|
||||
AuthClientSecurity struct {
|
||||
// Impersonates a specific user;
|
||||
// ignored when non client-credentials grant is used
|
||||
//ImpersonateUser uint64 `json:"impersonateUser,string,omitempty"`
|
||||
ImpersonateUser uint64 `json:"impersonateUser,string,omitempty"`
|
||||
|
||||
// Subset of roles, permitted to be used with this client
|
||||
// IDs are intentionally stored as strings to support JS (int64 only)
|
||||
@@ -146,7 +145,7 @@ func (set AuthClientSet) FindByHandle(handle string) *AuthClient {
|
||||
|
||||
func (r *AuthClient) Verify() error {
|
||||
switch {
|
||||
case !r.Enabled:
|
||||
case r == nil || !r.Enabled:
|
||||
return fmt.Errorf("disabled")
|
||||
case r.ExpiresAt != nil && r.ExpiresAt.After(time.Now()):
|
||||
return fmt.Errorf("expired")
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// Role - An organisation may have many roles. Roles may have many channels available. Access to channels may be shared between roles.
|
||||
Role struct {
|
||||
ID uint64 `json:"roleID,string"`
|
||||
Name string `json:"name"`
|
||||
|
||||
+32
-31
@@ -2,11 +2,11 @@
|
||||
|
||||
> An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications.
|
||||
|
||||
[![Build][Build-Status-Image]][Build-Status-Url] [![Codecov][codecov-image]][codecov-url] [![ReportCard][reportcard-image]][reportcard-url] [![GoDoc][godoc-image]][godoc-url] [![License][license-image]][license-url]
|
||||
[![Build][build-status-image]][build-status-url] [![Codecov][codecov-image]][codecov-url] [![ReportCard][reportcard-image]][reportcard-url] [![GoDoc][godoc-image]][godoc-url] [![License][license-image]][license-url]
|
||||
|
||||
## Protocol Flow
|
||||
|
||||
``` text
|
||||
```text
|
||||
+--------+ +---------------+
|
||||
| |--(A)- Authorization Request ->| Resource |
|
||||
| | | Owner |
|
||||
@@ -30,13 +30,13 @@
|
||||
|
||||
### Download and install
|
||||
|
||||
``` bash
|
||||
```bash
|
||||
go get -u -v github.com/go-oauth2/oauth2/v4/...
|
||||
```
|
||||
|
||||
### Create file `server.go`
|
||||
|
||||
``` go
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -95,7 +95,7 @@ func main() {
|
||||
|
||||
### Build and run
|
||||
|
||||
``` bash
|
||||
```bash
|
||||
go build server.go
|
||||
|
||||
./server
|
||||
@@ -105,24 +105,24 @@ go build server.go
|
||||
|
||||
[http://localhost:9096/token?grant_type=client_credentials&client_id=000000&client_secret=999999&scope=read](http://localhost:9096/token?grant_type=client_credentials&client_id=000000&client_secret=999999&scope=read)
|
||||
|
||||
``` json
|
||||
```json
|
||||
{
|
||||
"access_token": "J86XVRYSNFCFI233KXDL0Q",
|
||||
"expires_in": 7200,
|
||||
"scope": "read",
|
||||
"token_type": "Bearer"
|
||||
"access_token": "J86XVRYSNFCFI233KXDL0Q",
|
||||
"expires_in": 7200,
|
||||
"scope": "read",
|
||||
"token_type": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Easy to use
|
||||
* Based on the [RFC 6749](https://tools.ietf.org/html/rfc6749) implementation
|
||||
* Token storage support TTL
|
||||
* Support custom expiration time of the access token
|
||||
* Support custom extension field
|
||||
* Support custom scope
|
||||
* Support jwt to generate access tokens
|
||||
- Easy to use
|
||||
- Based on the [RFC 6749](https://tools.ietf.org/html/rfc6749) implementation
|
||||
- Token storage support TTL
|
||||
- Support custom expiration time of the access token
|
||||
- Support custom extension field
|
||||
- Support custom scope
|
||||
- Support jwt to generate access tokens
|
||||
|
||||
## Example
|
||||
|
||||
@@ -161,27 +161,28 @@ if !ok || !token.Valid {
|
||||
|
||||
## Store Implements
|
||||
|
||||
* [BuntDB](https://github.com/tidwall/buntdb)(default store)
|
||||
* [Redis](https://github.com/go-oauth2/redis)
|
||||
* [MongoDB](https://github.com/go-oauth2/mongo)
|
||||
* [MySQL](https://github.com/go-oauth2/mysql)
|
||||
* [MySQL (Provides both client and token store)](https://github.com/imrenagi/go-oauth2-mysql)
|
||||
* [PostgreSQL](https://github.com/vgarvardt/go-oauth2-pg)
|
||||
* [DynamoDB](https://github.com/contamobi/go-oauth2-dynamodb)
|
||||
* [XORM](https://github.com/techknowlogick/go-oauth2-xorm)
|
||||
* [GORM](https://github.com/techknowlogick/go-oauth2-gorm)
|
||||
* [Firestore](https://github.com/tslamic/go-oauth2-firestore)
|
||||
- [BuntDB](https://github.com/tidwall/buntdb)(default store)
|
||||
- [Redis](https://github.com/go-oauth2/redis)
|
||||
- [MongoDB](https://github.com/go-oauth2/mongo)
|
||||
- [MySQL](https://github.com/go-oauth2/mysql)
|
||||
- [MySQL (Provides both client and token store)](https://github.com/imrenagi/go-oauth2-mysql)
|
||||
- [PostgreSQL](https://github.com/vgarvardt/go-oauth2-pg)
|
||||
- [DynamoDB](https://github.com/contamobi/go-oauth2-dynamodb)
|
||||
- [XORM](https://github.com/techknowlogick/go-oauth2-xorm)
|
||||
- [XORM (MySQL, client and token store)](https://github.com/rainlay/go-oauth2-xorm)
|
||||
- [GORM](https://github.com/techknowlogick/go-oauth2-gorm)
|
||||
- [Firestore](https://github.com/tslamic/go-oauth2-firestore)
|
||||
|
||||
## Handy Utilities
|
||||
|
||||
* [OAuth2 Proxy Logger (Debug utility that proxies interfaces and logs)](https://github.com/aubelsb2/oauth2-logger-proxy)
|
||||
- [OAuth2 Proxy Logger (Debug utility that proxies interfaces and logs)](https://github.com/aubelsb2/oauth2-logger-proxy)
|
||||
|
||||
## MIT License
|
||||
|
||||
Copyright (c) 2016 Lyric
|
||||
Copyright (c) 2016 Lyric
|
||||
|
||||
[Build-Status-Url]: https://travis-ci.org/go-oauth2/oauth2
|
||||
[Build-Status-Image]: https://travis-ci.org/go-oauth2/oauth2.svg?branch=master
|
||||
[build-status-url]: https://travis-ci.org/go-oauth2/oauth2
|
||||
[build-status-image]: https://travis-ci.org/go-oauth2/oauth2.svg?branch=master
|
||||
[codecov-url]: https://codecov.io/gh/go-oauth2/oauth2
|
||||
[codecov-image]: https://codecov.io/gh/go-oauth2/oauth2/branch/master/graph/badge.svg
|
||||
[reportcard-url]: https://goreportcard.com/report/github.com/go-oauth2/oauth2/v4
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ type (
|
||||
ClientAuthorizedHandler func(clientID string, grant oauth2.GrantType) (allowed bool, err error)
|
||||
|
||||
// ClientScopeHandler check the client allows to use scope
|
||||
ClientScopeHandler func(clientID, scope string) (allowed bool, err error)
|
||||
ClientScopeHandler func(tgr *oauth2.TokenGenerateRequest) (allowed bool, err error)
|
||||
|
||||
// UserAuthorizationHandler get user id from request authorization
|
||||
UserAuthorizationHandler func(w http.ResponseWriter, r *http.Request) (userID string, err error)
|
||||
@@ -25,9 +25,9 @@ type (
|
||||
PasswordAuthorizationHandler func(username, password string) (userID string, err error)
|
||||
|
||||
// RefreshingScopeHandler check the scope of the refreshing token
|
||||
RefreshingScopeHandler func(newScope, oldScope string) (allowed bool, err error)
|
||||
RefreshingScopeHandler func(tgr *oauth2.TokenGenerateRequest, oldScope string) (allowed bool, err error)
|
||||
|
||||
//RefreshingValidationHandler check if refresh_token is still valid. eg no revocation or other
|
||||
// RefreshingValidationHandler check if refresh_token is still valid. eg no revocation or other
|
||||
RefreshingValidationHandler func(ti oauth2.TokenInfo) (allowed bool, err error)
|
||||
|
||||
// ResponseErrorHandler response error handing
|
||||
|
||||
+15
-9
@@ -214,7 +214,15 @@ func (s *Server) GetAuthorizeToken(ctx context.Context, req *AuthorizeRequest) (
|
||||
|
||||
// check the client allows the authorized scope
|
||||
if fn := s.ClientScopeHandler; fn != nil {
|
||||
allowed, err := fn(req.ClientID, req.Scope)
|
||||
tgr := &oauth2.TokenGenerateRequest{
|
||||
ClientID: req.ClientID,
|
||||
UserID: req.UserID,
|
||||
RedirectURI: req.RedirectURI,
|
||||
Scope: req.Scope,
|
||||
AccessTokenExp: req.AccessTokenExp,
|
||||
Request: req.Request,
|
||||
}
|
||||
allowed, err := fn(tgr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !allowed {
|
||||
@@ -311,11 +319,6 @@ func (s *Server) ValidationTokenRequest(r *http.Request) (oauth2.GrantType, *oau
|
||||
return "", nil, errors.ErrUnsupportedGrantType
|
||||
}
|
||||
|
||||
codeVer := r.FormValue("code_verifier")
|
||||
if s.Config.ForcePKCE && codeVer == "" {
|
||||
return "", nil, errors.ErrInvalidRequest
|
||||
}
|
||||
|
||||
clientID, clientSecret, err := s.ClientInfoHandler(r)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
@@ -335,7 +338,10 @@ func (s *Server) ValidationTokenRequest(r *http.Request) (oauth2.GrantType, *oau
|
||||
tgr.Code == "" {
|
||||
return "", nil, errors.ErrInvalidRequest
|
||||
}
|
||||
tgr.CodeVerifier = codeVer
|
||||
tgr.CodeVerifier = r.FormValue("code_verifier")
|
||||
if s.Config.ForcePKCE && tgr.CodeVerifier == "" {
|
||||
return "", nil, errors.ErrInvalidRequest
|
||||
}
|
||||
case oauth2.PasswordCredentials:
|
||||
tgr.Scope = r.FormValue("scope")
|
||||
username, password := r.FormValue("username"), r.FormValue("password")
|
||||
@@ -404,7 +410,7 @@ func (s *Server) GetAccessToken(ctx context.Context, gt oauth2.GrantType, tgr *o
|
||||
return ti, nil
|
||||
case oauth2.PasswordCredentials, oauth2.ClientCredentials:
|
||||
if fn := s.ClientScopeHandler; fn != nil {
|
||||
allowed, err := fn(tgr.ClientID, tgr.Scope)
|
||||
allowed, err := fn(tgr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !allowed {
|
||||
@@ -423,7 +429,7 @@ func (s *Server) GetAccessToken(ctx context.Context, gt oauth2.GrantType, tgr *o
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allowed, err := scopeFn(scope, rti.GetScope())
|
||||
allowed, err := scopeFn(tgr, rti.GetScope())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !allowed {
|
||||
|
||||
Vendored
+1
-3
@@ -82,7 +82,7 @@ github.com/go-chi/httprate
|
||||
# github.com/go-chi/jwtauth v0.0.0-20190109153619-47840abb19b3
|
||||
## explicit
|
||||
github.com/go-chi/jwtauth
|
||||
# github.com/go-oauth2/oauth2/v4 v4.2.0
|
||||
# github.com/go-oauth2/oauth2/v4 v4.3.0
|
||||
## explicit
|
||||
github.com/go-oauth2/oauth2/v4
|
||||
github.com/go-oauth2/oauth2/v4/errors
|
||||
@@ -212,8 +212,6 @@ github.com/prometheus/common/model
|
||||
# github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084
|
||||
github.com/prometheus/procfs
|
||||
github.com/prometheus/procfs/internal/fs
|
||||
# github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b
|
||||
## explicit
|
||||
# github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
## explicit
|
||||
github.com/rwcarlsen/goexif/exif
|
||||
|
||||
Reference in New Issue
Block a user