replace zxq.co/ripple/hanayo
This commit is contained in:
2
vendor/github.com/RangelReale/osin/.gitignore
generated
vendored
Normal file
2
vendor/github.com/RangelReale/osin/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.exe
|
||||
/src
|
7
vendor/github.com/RangelReale/osin/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/RangelReale/osin/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- tip
|
19
vendor/github.com/RangelReale/osin/CHANGELOG
generated
vendored
Normal file
19
vendor/github.com/RangelReale/osin/CHANGELOG
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
2014-06-25
|
||||
==========
|
||||
* BREAKING CHANGES:
|
||||
- Storage interface has 2 new methods, Clone and Close, to better support storages
|
||||
that need to clone / close in each connection (mgo)
|
||||
- Client was changed to be an interface instead of an struct. Because of that,
|
||||
the Storage interface also had to change, as interface is already a pointer.
|
||||
|
||||
- HOW TO FIX YOUR CODE:
|
||||
+ In your Storage, add a Clone function returning itself, and a do nothing Close.
|
||||
+ In your Storage, replace all *osin.Client with osin.Client (remove the pointer reference)
|
||||
+ If you used the osin.Client struct directly in your code, change it to osin.DefaultClient,
|
||||
which is a struct with the same fields that implements the interface.
|
||||
+ Change all accesses using osin.Client to use the methods instead of the fields directly.
|
||||
+ You MUST defer Response.Close in all your http handlers, otherwise some
|
||||
Storages may not clean correctly.
|
||||
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
9
vendor/github.com/RangelReale/osin/LICENSE
generated
vendored
Normal file
9
vendor/github.com/RangelReale/osin/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Copyright (c) 2013, Rangel Reale
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of the SIB IT nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
112
vendor/github.com/RangelReale/osin/README.md
generated
vendored
Normal file
112
vendor/github.com/RangelReale/osin/README.md
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
OSIN
|
||||
====
|
||||
|
||||
[](https://godoc.org/github.com/RangelReale/osin)
|
||||
|
||||
|
||||
Golang OAuth2 server library
|
||||
----------------------------
|
||||
|
||||
OSIN is an OAuth2 server library for the Go language, as specified at
|
||||
http://tools.ietf.org/html/rfc6749 and http://tools.ietf.org/html/draft-ietf-oauth-v2-10.
|
||||
|
||||
It also includes support for PKCE, as specified at https://tools.ietf.org/html/rfc7636,
|
||||
which increases security for code-exchange flows for public OAuth clients.
|
||||
|
||||
Using it, you can build your own OAuth2 authentication service.
|
||||
|
||||
The library implements the majority of the specification, like authorization and token endpoints, and authorization code, implicit, resource owner and client credentials grant types.
|
||||
|
||||
### Example Server
|
||||
|
||||
````go
|
||||
import (
|
||||
"github.com/RangelReale/osin"
|
||||
ex "github.com/RangelReale/osin/example"
|
||||
)
|
||||
|
||||
// ex.NewTestStorage implements the "osin.Storage" interface
|
||||
server := osin.NewServer(osin.NewServerConfig(), ex.NewTestStorage())
|
||||
|
||||
// Authorization code endpoint
|
||||
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
|
||||
// HANDLE LOGIN PAGE HERE
|
||||
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
http.ListenAndServe(":14000", nil)
|
||||
````
|
||||
|
||||
### Example Access
|
||||
|
||||
Open in your web browser:
|
||||
|
||||
````
|
||||
http://localhost:14000/authorize?response_type=code&client_id=1234&redirect_uri=http%3A%2F%2Flocalhost%3A14000%2Fappauth%2Fcode
|
||||
````
|
||||
|
||||
### Storage backends
|
||||
|
||||
There is a mock available at [example/teststorage.go](/example/teststorage.go) which you can use as a guide for writing your own.
|
||||
|
||||
You might want to check out other implementations for common database management systems as well:
|
||||
|
||||
* [PostgreSQL](https://github.com/ory-am/osin-storage)
|
||||
* [MongoDB](https://github.com/martint17r/osin-mongo-storage)
|
||||
* [RethinkDB](https://github.com/ahmet/osin-rethinkdb)
|
||||
* [DynamoDB](https://github.com/uniplaces/osin-dynamodb)
|
||||
* [Couchbase](https://github.com/elgris/osin-couchbase-storage)
|
||||
* [MySQL](https://github.com/felipeweb/osin-mysql)
|
||||
* [Redis](https://github.com/ShaleApps/osinredis)
|
||||
|
||||
### License
|
||||
|
||||
The code is licensed using "New BSD" license.
|
||||
|
||||
### Author
|
||||
|
||||
Rangel Reale
|
||||
rangelreale@gmail.com
|
||||
|
||||
### Changes
|
||||
|
||||
2014-06-25
|
||||
==========
|
||||
* BREAKING CHANGES:
|
||||
- Storage interface has 2 new methods, Clone and Close, to better support storages
|
||||
that need to clone / close in each connection (mgo)
|
||||
- Client was changed to be an interface instead of an struct. Because of that,
|
||||
the Storage interface also had to change, as interface is already a pointer.
|
||||
|
||||
- HOW TO FIX YOUR CODE:
|
||||
+ In your Storage, add a Clone function returning itself, and a do nothing Close.
|
||||
+ In your Storage, replace all *osin.Client with osin.Client (remove the pointer reference)
|
||||
+ If you used the osin.Client struct directly in your code, change it to osin.DefaultClient,
|
||||
which is a struct with the same fields that implements the interface.
|
||||
+ Change all accesses using osin.Client to use the methods instead of the fields directly.
|
||||
+ You MUST defer Response.Close in all your http handlers, otherwise some
|
||||
Storages may not clean correctly.
|
||||
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
562
vendor/github.com/RangelReale/osin/access.go
generated
vendored
Normal file
562
vendor/github.com/RangelReale/osin/access.go
generated
vendored
Normal file
@@ -0,0 +1,562 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AccessRequestType is the type for OAuth param `grant_type`
|
||||
type AccessRequestType string
|
||||
|
||||
const (
|
||||
AUTHORIZATION_CODE AccessRequestType = "authorization_code"
|
||||
REFRESH_TOKEN AccessRequestType = "refresh_token"
|
||||
PASSWORD AccessRequestType = "password"
|
||||
CLIENT_CREDENTIALS AccessRequestType = "client_credentials"
|
||||
ASSERTION AccessRequestType = "assertion"
|
||||
IMPLICIT AccessRequestType = "__implicit"
|
||||
)
|
||||
|
||||
// AccessRequest is a request for access tokens
|
||||
type AccessRequest struct {
|
||||
Type AccessRequestType
|
||||
Code string
|
||||
Client Client
|
||||
AuthorizeData *AuthorizeData
|
||||
AccessData *AccessData
|
||||
|
||||
// Force finish to use this access data, to allow access data reuse
|
||||
ForceAccessData *AccessData
|
||||
RedirectUri string
|
||||
Scope string
|
||||
Username string
|
||||
Password string
|
||||
AssertionType string
|
||||
Assertion string
|
||||
|
||||
// Set if request is authorized
|
||||
Authorized bool
|
||||
|
||||
// Token expiration in seconds. Change if different from default
|
||||
Expiration int32
|
||||
|
||||
// Set if a refresh token should be generated
|
||||
GenerateRefresh bool
|
||||
|
||||
// Data to be passed to storage. Not used by the library.
|
||||
UserData interface{}
|
||||
|
||||
// HttpRequest *http.Request for special use
|
||||
HttpRequest *http.Request
|
||||
|
||||
// Optional code_verifier as described in rfc7636
|
||||
CodeVerifier string
|
||||
}
|
||||
|
||||
// AccessData represents an access grant (tokens, expiration, client, etc)
|
||||
type AccessData struct {
|
||||
// Client information
|
||||
Client Client
|
||||
|
||||
// Authorize data, for authorization code
|
||||
AuthorizeData *AuthorizeData
|
||||
|
||||
// Previous access data, for refresh token
|
||||
AccessData *AccessData
|
||||
|
||||
// Access token
|
||||
AccessToken string
|
||||
|
||||
// Refresh Token. Can be blank
|
||||
RefreshToken string
|
||||
|
||||
// Token expiration in seconds
|
||||
ExpiresIn int32
|
||||
|
||||
// Requested scope
|
||||
Scope string
|
||||
|
||||
// Redirect Uri from request
|
||||
RedirectUri string
|
||||
|
||||
// Date created
|
||||
CreatedAt time.Time
|
||||
|
||||
// Data to be passed to storage. Not used by the library.
|
||||
UserData interface{}
|
||||
}
|
||||
|
||||
// IsExpired returns true if access expired
|
||||
func (d *AccessData) IsExpired() bool {
|
||||
return d.IsExpiredAt(time.Now())
|
||||
}
|
||||
|
||||
// IsExpiredAt returns true if access expires at time 't'
|
||||
func (d *AccessData) IsExpiredAt(t time.Time) bool {
|
||||
return d.ExpireAt().Before(t)
|
||||
}
|
||||
|
||||
// ExpireAt returns the expiration date
|
||||
func (d *AccessData) ExpireAt() time.Time {
|
||||
return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
|
||||
}
|
||||
|
||||
// AccessTokenGen generates access tokens
|
||||
type AccessTokenGen interface {
|
||||
GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error)
|
||||
}
|
||||
|
||||
// HandleAccessRequest is the http.HandlerFunc for handling access token requests
|
||||
func (s *Server) HandleAccessRequest(w *Response, r *http.Request) *AccessRequest {
|
||||
// Only allow GET or POST
|
||||
if r.Method == "GET" {
|
||||
if !s.Config.AllowGetAccessRequest {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = errors.New("Request must be POST")
|
||||
return nil
|
||||
}
|
||||
} else if r.Method != "POST" {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = errors.New("Request must be POST")
|
||||
return nil
|
||||
}
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
|
||||
grantType := AccessRequestType(r.Form.Get("grant_type"))
|
||||
if s.Config.AllowedAccessTypes.Exists(grantType) {
|
||||
switch grantType {
|
||||
case AUTHORIZATION_CODE:
|
||||
return s.handleAuthorizationCodeRequest(w, r)
|
||||
case REFRESH_TOKEN:
|
||||
return s.handleRefreshTokenRequest(w, r)
|
||||
case PASSWORD:
|
||||
return s.handlePasswordRequest(w, r)
|
||||
case CLIENT_CREDENTIALS:
|
||||
return s.handleClientCredentialsRequest(w, r)
|
||||
case ASSERTION:
|
||||
return s.handleAssertionRequest(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
w.SetError(E_UNSUPPORTED_GRANT_TYPE, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleAuthorizationCodeRequest(w *Response, r *http.Request) *AccessRequest {
|
||||
// get client authentication
|
||||
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate access token
|
||||
ret := &AccessRequest{
|
||||
Type: AUTHORIZATION_CODE,
|
||||
Code: r.Form.Get("code"),
|
||||
CodeVerifier: r.Form.Get("code_verifier"),
|
||||
RedirectUri: r.Form.Get("redirect_uri"),
|
||||
GenerateRefresh: true,
|
||||
Expiration: s.Config.AccessExpiration,
|
||||
HttpRequest: r,
|
||||
}
|
||||
|
||||
// "code" is required
|
||||
if ret.Code == "" {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// must have a valid client
|
||||
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// must be a valid authorization code
|
||||
var err error
|
||||
ret.AuthorizeData, err = w.Storage.LoadAuthorize(ret.Code)
|
||||
if err != nil {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if ret.AuthorizeData == nil {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AuthorizeData.Client == nil {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AuthorizeData.Client.GetRedirectUri() == "" {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AuthorizeData.IsExpiredAt(s.Now()) {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// code must be from the client
|
||||
if ret.AuthorizeData.Client.GetId() != ret.Client.GetId() {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// check redirect uri
|
||||
if ret.RedirectUri == "" {
|
||||
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
|
||||
}
|
||||
if err = ValidateUriList(ret.Client.GetRedirectUri(), ret.RedirectUri, s.Config.RedirectUriSeparator); err != nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if ret.AuthorizeData.RedirectUri != ret.RedirectUri {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = errors.New("Redirect uri is different")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify PKCE, if present in the authorization data
|
||||
if len(ret.AuthorizeData.CodeChallenge) > 0 {
|
||||
// https://tools.ietf.org/html/rfc7636#section-4.1
|
||||
if matched := pkceMatcher.MatchString(ret.CodeVerifier); !matched {
|
||||
w.SetError(E_INVALID_REQUEST, "code_verifier invalid (rfc7636)")
|
||||
w.InternalError = errors.New("code_verifier has invalid format")
|
||||
return nil
|
||||
}
|
||||
|
||||
// https: //tools.ietf.org/html/rfc7636#section-4.6
|
||||
codeVerifier := ""
|
||||
switch ret.AuthorizeData.CodeChallengeMethod {
|
||||
case "", PKCE_PLAIN:
|
||||
codeVerifier = ret.CodeVerifier
|
||||
case PKCE_S256:
|
||||
hash := sha256.Sum256([]byte(ret.CodeVerifier))
|
||||
codeVerifier = base64.RawURLEncoding.EncodeToString(hash[:])
|
||||
default:
|
||||
w.SetError(E_INVALID_REQUEST, "code_challenge_method transform algorithm not supported (rfc7636)")
|
||||
return nil
|
||||
}
|
||||
if codeVerifier != ret.AuthorizeData.CodeChallenge {
|
||||
w.SetError(E_INVALID_GRANT, "code_verifier invalid (rfc7636)")
|
||||
w.InternalError = errors.New("code_verifier failed comparison with code_challenge")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// set rest of data
|
||||
ret.Scope = ret.AuthorizeData.Scope
|
||||
ret.UserData = ret.AuthorizeData.UserData
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func extraScopes(access_scopes, refresh_scopes string) bool {
|
||||
access_scopes_list := strings.Split(access_scopes, " ")
|
||||
refresh_scopes_list := strings.Split(refresh_scopes, " ")
|
||||
|
||||
access_map := make(map[string]int)
|
||||
|
||||
for _, scope := range access_scopes_list {
|
||||
if scope == "" {
|
||||
continue
|
||||
}
|
||||
access_map[scope] = 1
|
||||
}
|
||||
|
||||
for _, scope := range refresh_scopes_list {
|
||||
if scope == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := access_map[scope]; !ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) handleRefreshTokenRequest(w *Response, r *http.Request) *AccessRequest {
|
||||
// get client authentication
|
||||
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate access token
|
||||
ret := &AccessRequest{
|
||||
Type: REFRESH_TOKEN,
|
||||
Code: r.Form.Get("refresh_token"),
|
||||
Scope: r.Form.Get("scope"),
|
||||
GenerateRefresh: true,
|
||||
Expiration: s.Config.AccessExpiration,
|
||||
HttpRequest: r,
|
||||
}
|
||||
|
||||
// "refresh_token" is required
|
||||
if ret.Code == "" {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// must have a valid client
|
||||
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// must be a valid refresh code
|
||||
var err error
|
||||
ret.AccessData, err = w.Storage.LoadRefresh(ret.Code)
|
||||
if err != nil {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData == nil {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData.Client == nil {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData.Client.GetRedirectUri() == "" {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// client must be the same as the previous token
|
||||
if ret.AccessData.Client.GetId() != ret.Client.GetId() {
|
||||
w.SetError(E_INVALID_CLIENT, "")
|
||||
w.InternalError = errors.New("Client id must be the same from previous token")
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// set rest of data
|
||||
ret.RedirectUri = ret.AccessData.RedirectUri
|
||||
ret.UserData = ret.AccessData.UserData
|
||||
if ret.Scope == "" {
|
||||
ret.Scope = ret.AccessData.Scope
|
||||
}
|
||||
|
||||
if extraScopes(ret.AccessData.Scope, ret.Scope) {
|
||||
w.SetError(E_ACCESS_DENIED, "")
|
||||
w.InternalError = errors.New("the requested scope must not include any scope not originally granted by the resource owner")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Server) handlePasswordRequest(w *Response, r *http.Request) *AccessRequest {
|
||||
// get client authentication
|
||||
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate access token
|
||||
ret := &AccessRequest{
|
||||
Type: PASSWORD,
|
||||
Username: r.Form.Get("username"),
|
||||
Password: r.Form.Get("password"),
|
||||
Scope: r.Form.Get("scope"),
|
||||
GenerateRefresh: true,
|
||||
Expiration: s.Config.AccessExpiration,
|
||||
HttpRequest: r,
|
||||
}
|
||||
|
||||
// "username" and "password" is required
|
||||
if ret.Username == "" || ret.Password == "" {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// must have a valid client
|
||||
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// set redirect uri
|
||||
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Server) handleClientCredentialsRequest(w *Response, r *http.Request) *AccessRequest {
|
||||
// get client authentication
|
||||
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate access token
|
||||
ret := &AccessRequest{
|
||||
Type: CLIENT_CREDENTIALS,
|
||||
Scope: r.Form.Get("scope"),
|
||||
GenerateRefresh: false,
|
||||
Expiration: s.Config.AccessExpiration,
|
||||
HttpRequest: r,
|
||||
}
|
||||
|
||||
// must have a valid client
|
||||
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// set redirect uri
|
||||
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Server) handleAssertionRequest(w *Response, r *http.Request) *AccessRequest {
|
||||
// get client authentication
|
||||
auth := getClientAuth(w, r, s.Config.AllowClientSecretInParams)
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate access token
|
||||
ret := &AccessRequest{
|
||||
Type: ASSERTION,
|
||||
Scope: r.Form.Get("scope"),
|
||||
AssertionType: r.Form.Get("assertion_type"),
|
||||
Assertion: r.Form.Get("assertion"),
|
||||
GenerateRefresh: false, // assertion should NOT generate a refresh token, per the RFC
|
||||
Expiration: s.Config.AccessExpiration,
|
||||
HttpRequest: r,
|
||||
}
|
||||
|
||||
// "assertion_type" and "assertion" is required
|
||||
if ret.AssertionType == "" || ret.Assertion == "" {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// must have a valid client
|
||||
if ret.Client = getClient(auth, w.Storage, w); ret.Client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// set redirect uri
|
||||
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Server) FinishAccessRequest(w *Response, r *http.Request, ar *AccessRequest) {
|
||||
// don't process if is already an error
|
||||
if w.IsError {
|
||||
return
|
||||
}
|
||||
redirectUri := r.Form.Get("redirect_uri")
|
||||
// Get redirect uri from AccessRequest if it's there (e.g., refresh token request)
|
||||
if ar.RedirectUri != "" {
|
||||
redirectUri = ar.RedirectUri
|
||||
}
|
||||
if ar.Authorized {
|
||||
var ret *AccessData
|
||||
var err error
|
||||
|
||||
if ar.ForceAccessData == nil {
|
||||
// generate access token
|
||||
ret = &AccessData{
|
||||
Client: ar.Client,
|
||||
AuthorizeData: ar.AuthorizeData,
|
||||
AccessData: ar.AccessData,
|
||||
RedirectUri: redirectUri,
|
||||
CreatedAt: s.Now(),
|
||||
ExpiresIn: ar.Expiration,
|
||||
UserData: ar.UserData,
|
||||
Scope: ar.Scope,
|
||||
}
|
||||
|
||||
// generate access token
|
||||
ret.AccessToken, ret.RefreshToken, err = s.AccessTokenGen.GenerateAccessToken(ret, ar.GenerateRefresh)
|
||||
if err != nil {
|
||||
w.SetError(E_SERVER_ERROR, "")
|
||||
w.InternalError = err
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ret = ar.ForceAccessData
|
||||
}
|
||||
|
||||
// save access token
|
||||
if err = w.Storage.SaveAccess(ret); err != nil {
|
||||
w.SetError(E_SERVER_ERROR, "")
|
||||
w.InternalError = err
|
||||
return
|
||||
}
|
||||
|
||||
// remove authorization token
|
||||
if ret.AuthorizeData != nil {
|
||||
w.Storage.RemoveAuthorize(ret.AuthorizeData.Code)
|
||||
}
|
||||
|
||||
// remove previous access token
|
||||
if ret.AccessData != nil && !s.Config.RetainTokenAfterRefresh {
|
||||
if ret.AccessData.RefreshToken != "" {
|
||||
w.Storage.RemoveRefresh(ret.AccessData.RefreshToken)
|
||||
}
|
||||
w.Storage.RemoveAccess(ret.AccessData.AccessToken)
|
||||
}
|
||||
|
||||
// output data
|
||||
w.Output["access_token"] = ret.AccessToken
|
||||
w.Output["token_type"] = s.Config.TokenType
|
||||
w.Output["expires_in"] = ret.ExpiresIn
|
||||
if ret.RefreshToken != "" {
|
||||
w.Output["refresh_token"] = ret.RefreshToken
|
||||
}
|
||||
if ret.Scope != "" {
|
||||
w.Output["scope"] = ret.Scope
|
||||
}
|
||||
} else {
|
||||
w.SetError(E_ACCESS_DENIED, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
// getClient looks up and authenticates the basic auth using the given
|
||||
// storage. Sets an error on the response if auth fails or a server error occurs.
|
||||
func getClient(auth *BasicAuth, storage Storage, w *Response) Client {
|
||||
client, err := storage.GetClient(auth.Username)
|
||||
if err == ErrNotFound {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
w.SetError(E_SERVER_ERROR, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if client == nil {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
if !CheckClientSecret(client, auth.Password) {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
if client.GetRedirectUri() == "" {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
return client
|
||||
}
|
489
vendor/github.com/RangelReale/osin/access_test.go
generated
vendored
Normal file
489
vendor/github.com/RangelReale/osin/access_test.go
generated
vendored
Normal file
@@ -0,0 +1,489 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAccessAuthorizationCode(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAccessTypes = AllowedAccessType{AUTHORIZATION_CODE}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.SetBasicAuth("1234", "aabbccdd")
|
||||
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("grant_type", string(AUTHORIZATION_CODE))
|
||||
req.Form.Set("code", "9999")
|
||||
req.Form.Set("state", "a")
|
||||
req.PostForm = make(url.Values)
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("Unexpected access token: %s", d)
|
||||
}
|
||||
|
||||
if d := resp.Output["refresh_token"]; d != "r1" {
|
||||
t.Fatalf("Unexpected refresh token: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessRefreshToken(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.SetBasicAuth("1234", "aabbccdd")
|
||||
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("grant_type", string(REFRESH_TOKEN))
|
||||
req.Form.Set("refresh_token", "r9999")
|
||||
req.Form.Set("state", "a")
|
||||
req.PostForm = make(url.Values)
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, req, ar)
|
||||
}
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if _, err := server.Storage.LoadRefresh("r9999"); err == nil {
|
||||
t.Fatalf("token was not deleted")
|
||||
}
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("Unexpected access token: %s", d)
|
||||
}
|
||||
|
||||
if d := resp.Output["refresh_token"]; d != "r1" {
|
||||
t.Fatalf("Unexpected refresh token: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessRefreshTokenSaveToken(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
server.Config.RetainTokenAfterRefresh = true
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.SetBasicAuth("1234", "aabbccdd")
|
||||
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("grant_type", string(REFRESH_TOKEN))
|
||||
req.Form.Set("refresh_token", "r9999")
|
||||
req.Form.Set("state", "a")
|
||||
req.PostForm = make(url.Values)
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, req, ar)
|
||||
}
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if _, err := server.Storage.LoadRefresh("r9999"); err != nil {
|
||||
t.Fatalf("token incorrectly deleted: %s", err.Error())
|
||||
}
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("Unexpected access token: %s", d)
|
||||
}
|
||||
|
||||
if d := resp.Output["refresh_token"]; d != "r1" {
|
||||
t.Fatalf("Unexpected refresh token: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessPassword(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAccessTypes = AllowedAccessType{PASSWORD}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.SetBasicAuth("1234", "aabbccdd")
|
||||
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("grant_type", string(PASSWORD))
|
||||
req.Form.Set("username", "testing")
|
||||
req.Form.Set("password", "testing")
|
||||
req.Form.Set("state", "a")
|
||||
req.PostForm = make(url.Values)
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, req); ar != nil {
|
||||
ar.Authorized = ar.Username == "testing" && ar.Password == "testing"
|
||||
server.FinishAccessRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("Unexpected access token: %s", d)
|
||||
}
|
||||
|
||||
if d := resp.Output["refresh_token"]; d != "r1" {
|
||||
t.Fatalf("Unexpected refresh token: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessClientCredentials(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAccessTypes = AllowedAccessType{CLIENT_CREDENTIALS}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.SetBasicAuth("1234", "aabbccdd")
|
||||
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("grant_type", string(CLIENT_CREDENTIALS))
|
||||
req.Form.Set("state", "a")
|
||||
req.PostForm = make(url.Values)
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("Unexpected access token: %s", d)
|
||||
}
|
||||
|
||||
if d, dok := resp.Output["refresh_token"]; dok {
|
||||
t.Fatalf("Refresh token should not be generated: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtraScopes(t *testing.T) {
|
||||
if extraScopes("", "") == true {
|
||||
t.Fatalf("extraScopes returned true with empty scopes")
|
||||
}
|
||||
|
||||
if extraScopes("a", "") == true {
|
||||
t.Fatalf("extraScopes returned true with less scopes")
|
||||
}
|
||||
|
||||
if extraScopes("a,b", "b,a") == true {
|
||||
t.Fatalf("extraScopes returned true with matching scopes")
|
||||
}
|
||||
|
||||
if extraScopes("a,b", "b,a,c") == false {
|
||||
t.Fatalf("extraScopes returned false with extra scopes")
|
||||
}
|
||||
|
||||
if extraScopes("", "a") == false {
|
||||
t.Fatalf("extraScopes returned false with extra scopes")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// clientWithoutMatcher just implements the base Client interface
|
||||
type clientWithoutMatcher struct {
|
||||
Id string
|
||||
Secret string
|
||||
RedirectUri string
|
||||
}
|
||||
|
||||
func (c *clientWithoutMatcher) GetId() string { return c.Id }
|
||||
func (c *clientWithoutMatcher) GetSecret() string { return c.Secret }
|
||||
func (c *clientWithoutMatcher) GetRedirectUri() string { return c.RedirectUri }
|
||||
func (c *clientWithoutMatcher) GetUserData() interface{} { return nil }
|
||||
|
||||
func TestGetClientWithoutMatcher(t *testing.T) {
|
||||
myclient := &clientWithoutMatcher{
|
||||
Id: "myclient",
|
||||
Secret: "myclientsecret",
|
||||
RedirectUri: "http://www.example.com",
|
||||
}
|
||||
storage := &TestingStorage{clients: map[string]Client{myclient.Id: myclient}}
|
||||
|
||||
// Ensure bad secret fails
|
||||
{
|
||||
auth := &BasicAuth{
|
||||
Username: "myclient",
|
||||
Password: "invalidsecret",
|
||||
}
|
||||
w := &Response{}
|
||||
client := getClient(auth, storage, w)
|
||||
if client != nil {
|
||||
t.Errorf("Expected error, got client: %v", client)
|
||||
}
|
||||
|
||||
if !w.IsError {
|
||||
t.Error("No error in response")
|
||||
}
|
||||
|
||||
if w.ErrorId != E_UNAUTHORIZED_CLIENT {
|
||||
t.Errorf("Expected error %v, got %v", E_UNAUTHORIZED_CLIENT, w.ErrorId)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure nonexistent client fails
|
||||
{
|
||||
auth := &BasicAuth{
|
||||
Username: "nonexistent",
|
||||
Password: "nonexistent",
|
||||
}
|
||||
w := &Response{}
|
||||
client := getClient(auth, storage, w)
|
||||
if client != nil {
|
||||
t.Errorf("Expected error, got client: %v", client)
|
||||
}
|
||||
|
||||
if !w.IsError {
|
||||
t.Error("No error in response")
|
||||
}
|
||||
|
||||
if w.ErrorId != E_UNAUTHORIZED_CLIENT {
|
||||
t.Errorf("Expected error %v, got %v", E_UNAUTHORIZED_CLIENT, w.ErrorId)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure good secret works
|
||||
{
|
||||
auth := &BasicAuth{
|
||||
Username: "myclient",
|
||||
Password: "myclientsecret",
|
||||
}
|
||||
w := &Response{}
|
||||
client := getClient(auth, storage, w)
|
||||
if client != myclient {
|
||||
t.Errorf("Expected client, got nil with response: %v", w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clientWithMatcher implements the base Client interface and the ClientSecretMatcher interface
|
||||
type clientWithMatcher struct {
|
||||
Id string
|
||||
Secret string
|
||||
RedirectUri string
|
||||
}
|
||||
|
||||
func (c *clientWithMatcher) GetId() string { return c.Id }
|
||||
func (c *clientWithMatcher) GetSecret() string { panic("called GetSecret"); return "" }
|
||||
func (c *clientWithMatcher) GetRedirectUri() string { return c.RedirectUri }
|
||||
func (c *clientWithMatcher) GetUserData() interface{} { return nil }
|
||||
func (c *clientWithMatcher) ClientSecretMatches(secret string) bool {
|
||||
return secret == c.Secret
|
||||
}
|
||||
|
||||
func TestGetClientSecretMatcher(t *testing.T) {
|
||||
myclient := &clientWithMatcher{
|
||||
Id: "myclient",
|
||||
Secret: "myclientsecret",
|
||||
RedirectUri: "http://www.example.com",
|
||||
}
|
||||
storage := &TestingStorage{clients: map[string]Client{myclient.Id: myclient}}
|
||||
|
||||
// Ensure bad secret fails, but does not panic (doesn't call GetSecret)
|
||||
{
|
||||
auth := &BasicAuth{
|
||||
Username: "myclient",
|
||||
Password: "invalidsecret",
|
||||
}
|
||||
w := &Response{}
|
||||
client := getClient(auth, storage, w)
|
||||
if client != nil {
|
||||
t.Errorf("Expected error, got client: %v", client)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure good secret works, but does not panic (doesn't call GetSecret)
|
||||
{
|
||||
auth := &BasicAuth{
|
||||
Username: "myclient",
|
||||
Password: "myclientsecret",
|
||||
}
|
||||
w := &Response{}
|
||||
client := getClient(auth, storage, w)
|
||||
if client != myclient {
|
||||
t.Errorf("Expected client, got nil with response: %v", w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessAuthorizationCodePKCE(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
Challenge string
|
||||
ChallengeMethod string
|
||||
Verifier string
|
||||
ExpectedError string
|
||||
}{
|
||||
"good, plain": {
|
||||
Challenge: "12345678901234567890123456789012345678901234567890",
|
||||
Verifier: "12345678901234567890123456789012345678901234567890",
|
||||
},
|
||||
"bad, plain": {
|
||||
Challenge: "12345678901234567890123456789012345678901234567890",
|
||||
Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
ExpectedError: "invalid_grant",
|
||||
},
|
||||
"good, S256": {
|
||||
Challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
|
||||
ChallengeMethod: "S256",
|
||||
Verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
||||
},
|
||||
"bad, S256": {
|
||||
Challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
|
||||
ChallengeMethod: "S256",
|
||||
Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
ExpectedError: "invalid_grant",
|
||||
},
|
||||
"missing from storage": {
|
||||
Challenge: "",
|
||||
ChallengeMethod: "",
|
||||
Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
},
|
||||
}
|
||||
|
||||
for k, test := range testcases {
|
||||
testStorage := NewTestingStorage()
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAccessTypes = AllowedAccessType{AUTHORIZATION_CODE}
|
||||
server := NewServer(sconfig, testStorage)
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
server.Storage.SaveAuthorize(&AuthorizeData{
|
||||
Client: testStorage.clients["public-client"],
|
||||
Code: "pkce-code",
|
||||
ExpiresIn: 3600,
|
||||
CreatedAt: time.Now(),
|
||||
RedirectUri: "http://localhost:14000/appauth",
|
||||
CodeChallenge: test.Challenge,
|
||||
CodeChallengeMethod: test.ChallengeMethod,
|
||||
})
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.SetBasicAuth("public-client", "")
|
||||
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("grant_type", string(AUTHORIZATION_CODE))
|
||||
req.Form.Set("code", "pkce-code")
|
||||
req.Form.Set("state", "a")
|
||||
req.Form.Set("code_verifier", test.Verifier)
|
||||
req.PostForm = make(url.Values)
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
if test.ExpectedError == "" || test.ExpectedError != resp.ErrorId {
|
||||
t.Errorf("%s: unexpected error: %v, %v", k, resp.ErrorId, resp.StatusText)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if test.ExpectedError == "" {
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("%s: Response should be data", k)
|
||||
}
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("%s: Unexpected access token: %s", k, d)
|
||||
}
|
||||
if d := resp.Output["refresh_token"]; d != "r1" {
|
||||
t.Fatalf("%s: Unexpected refresh token: %s", k, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
275
vendor/github.com/RangelReale/osin/authorize.go
generated
vendored
Normal file
275
vendor/github.com/RangelReale/osin/authorize.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AuthorizeRequestType is the type for OAuth param `response_type`
|
||||
type AuthorizeRequestType string
|
||||
|
||||
const (
|
||||
CODE AuthorizeRequestType = "code"
|
||||
TOKEN AuthorizeRequestType = "token"
|
||||
|
||||
PKCE_PLAIN = "plain"
|
||||
PKCE_S256 = "S256"
|
||||
)
|
||||
|
||||
var (
|
||||
pkceMatcher = regexp.MustCompile("^[a-zA-Z0-9~._-]{43,128}$")
|
||||
)
|
||||
|
||||
// Authorize request information
|
||||
type AuthorizeRequest struct {
|
||||
Type AuthorizeRequestType
|
||||
Client Client
|
||||
Scope string
|
||||
RedirectUri string
|
||||
State string
|
||||
|
||||
// Set if request is authorized
|
||||
Authorized bool
|
||||
|
||||
// Token expiration in seconds. Change if different from default.
|
||||
// If type = TOKEN, this expiration will be for the ACCESS token.
|
||||
Expiration int32
|
||||
|
||||
// Data to be passed to storage. Not used by the library.
|
||||
UserData interface{}
|
||||
|
||||
// HttpRequest *http.Request for special use
|
||||
HttpRequest *http.Request
|
||||
|
||||
// Optional code_challenge as described in rfc7636
|
||||
CodeChallenge string
|
||||
// Optional code_challenge_method as described in rfc7636
|
||||
CodeChallengeMethod string
|
||||
}
|
||||
|
||||
// Authorization data
|
||||
type AuthorizeData struct {
|
||||
// Client information
|
||||
Client Client
|
||||
|
||||
// Authorization code
|
||||
Code string
|
||||
|
||||
// Token expiration in seconds
|
||||
ExpiresIn int32
|
||||
|
||||
// Requested scope
|
||||
Scope string
|
||||
|
||||
// Redirect Uri from request
|
||||
RedirectUri string
|
||||
|
||||
// State data from request
|
||||
State string
|
||||
|
||||
// Date created
|
||||
CreatedAt time.Time
|
||||
|
||||
// Data to be passed to storage. Not used by the library.
|
||||
UserData interface{}
|
||||
|
||||
// Optional code_challenge as described in rfc7636
|
||||
CodeChallenge string
|
||||
// Optional code_challenge_method as described in rfc7636
|
||||
CodeChallengeMethod string
|
||||
}
|
||||
|
||||
// IsExpired is true if authorization expired
|
||||
func (d *AuthorizeData) IsExpired() bool {
|
||||
return d.IsExpiredAt(time.Now())
|
||||
}
|
||||
|
||||
// IsExpired is true if authorization expires at time 't'
|
||||
func (d *AuthorizeData) IsExpiredAt(t time.Time) bool {
|
||||
return d.ExpireAt().Before(t)
|
||||
}
|
||||
|
||||
// ExpireAt returns the expiration date
|
||||
func (d *AuthorizeData) ExpireAt() time.Time {
|
||||
return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
|
||||
}
|
||||
|
||||
// AuthorizeTokenGen is the token generator interface
|
||||
type AuthorizeTokenGen interface {
|
||||
GenerateAuthorizeToken(data *AuthorizeData) (string, error)
|
||||
}
|
||||
|
||||
// HandleAuthorizeRequest is the main http.HandlerFunc for handling
|
||||
// authorization requests
|
||||
func (s *Server) HandleAuthorizeRequest(w *Response, r *http.Request) *AuthorizeRequest {
|
||||
r.ParseForm()
|
||||
|
||||
// create the authorization request
|
||||
unescapedUri, err := url.QueryUnescape(r.Form.Get("redirect_uri"))
|
||||
if err != nil {
|
||||
w.SetErrorState(E_INVALID_REQUEST, "", "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := &AuthorizeRequest{
|
||||
State: r.Form.Get("state"),
|
||||
Scope: r.Form.Get("scope"),
|
||||
RedirectUri: unescapedUri,
|
||||
Authorized: false,
|
||||
HttpRequest: r,
|
||||
}
|
||||
|
||||
// must have a valid client
|
||||
ret.Client, err = w.Storage.GetClient(r.Form.Get("client_id"))
|
||||
if err == ErrNotFound {
|
||||
w.SetErrorState(E_UNAUTHORIZED_CLIENT, "", ret.State)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
w.SetErrorState(E_SERVER_ERROR, "", ret.State)
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if ret.Client == nil {
|
||||
w.SetErrorState(E_UNAUTHORIZED_CLIENT, "", ret.State)
|
||||
return nil
|
||||
}
|
||||
if ret.Client.GetRedirectUri() == "" {
|
||||
w.SetErrorState(E_UNAUTHORIZED_CLIENT, "", ret.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
// check redirect uri, if there are multiple client redirect uri's
|
||||
// don't set the uri
|
||||
if ret.RedirectUri == "" && FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator) == ret.Client.GetRedirectUri() {
|
||||
ret.RedirectUri = FirstUri(ret.Client.GetRedirectUri(), s.Config.RedirectUriSeparator)
|
||||
}
|
||||
|
||||
if err = ValidateUriList(ret.Client.GetRedirectUri(), ret.RedirectUri, s.Config.RedirectUriSeparator); err != nil {
|
||||
w.SetErrorState(E_INVALID_REQUEST, "", ret.State)
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
|
||||
w.SetRedirect(ret.RedirectUri)
|
||||
|
||||
requestType := AuthorizeRequestType(r.Form.Get("response_type"))
|
||||
if s.Config.AllowedAuthorizeTypes.Exists(requestType) {
|
||||
switch requestType {
|
||||
case CODE:
|
||||
ret.Type = CODE
|
||||
ret.Expiration = s.Config.AuthorizationExpiration
|
||||
|
||||
// Optional PKCE support (https://tools.ietf.org/html/rfc7636)
|
||||
if codeChallenge := r.Form.Get("code_challenge"); len(codeChallenge) == 0 {
|
||||
if s.Config.RequirePKCEForPublicClients && CheckClientSecret(ret.Client, "") {
|
||||
// https://tools.ietf.org/html/rfc7636#section-4.4.1
|
||||
w.SetErrorState(E_INVALID_REQUEST, "code_challenge (rfc7636) required for public clients", ret.State)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
codeChallengeMethod := r.Form.Get("code_challenge_method")
|
||||
// allowed values are "plain" (default) and "S256", per https://tools.ietf.org/html/rfc7636#section-4.3
|
||||
if len(codeChallengeMethod) == 0 {
|
||||
codeChallengeMethod = PKCE_PLAIN
|
||||
}
|
||||
if codeChallengeMethod != PKCE_PLAIN && codeChallengeMethod != PKCE_S256 {
|
||||
// https://tools.ietf.org/html/rfc7636#section-4.4.1
|
||||
w.SetErrorState(E_INVALID_REQUEST, "code_challenge_method transform algorithm not supported (rfc7636)", ret.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc7636#section-4.2
|
||||
if matched := pkceMatcher.MatchString(codeChallenge); !matched {
|
||||
w.SetErrorState(E_INVALID_REQUEST, "code_challenge invalid (rfc7636)", ret.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
ret.CodeChallenge = codeChallenge
|
||||
ret.CodeChallengeMethod = codeChallengeMethod
|
||||
}
|
||||
|
||||
case TOKEN:
|
||||
ret.Type = TOKEN
|
||||
ret.Expiration = s.Config.AccessExpiration
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
w.SetErrorState(E_UNSUPPORTED_RESPONSE_TYPE, "", ret.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) FinishAuthorizeRequest(w *Response, r *http.Request, ar *AuthorizeRequest) {
|
||||
// don't process if is already an error
|
||||
if w.IsError {
|
||||
return
|
||||
}
|
||||
|
||||
// force redirect response
|
||||
w.SetRedirect(ar.RedirectUri)
|
||||
|
||||
if ar.Authorized {
|
||||
if ar.Type == TOKEN {
|
||||
w.SetRedirectFragment(true)
|
||||
|
||||
// generate token directly
|
||||
ret := &AccessRequest{
|
||||
Type: IMPLICIT,
|
||||
Code: "",
|
||||
Client: ar.Client,
|
||||
RedirectUri: ar.RedirectUri,
|
||||
Scope: ar.Scope,
|
||||
GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case
|
||||
Authorized: true,
|
||||
Expiration: ar.Expiration,
|
||||
UserData: ar.UserData,
|
||||
}
|
||||
|
||||
s.FinishAccessRequest(w, r, ret)
|
||||
if ar.State != "" && w.InternalError == nil {
|
||||
w.Output["state"] = ar.State
|
||||
}
|
||||
} else {
|
||||
// generate authorization token
|
||||
ret := &AuthorizeData{
|
||||
Client: ar.Client,
|
||||
CreatedAt: s.Now(),
|
||||
ExpiresIn: ar.Expiration,
|
||||
RedirectUri: ar.RedirectUri,
|
||||
State: ar.State,
|
||||
Scope: ar.Scope,
|
||||
UserData: ar.UserData,
|
||||
// Optional PKCE challenge
|
||||
CodeChallenge: ar.CodeChallenge,
|
||||
CodeChallengeMethod: ar.CodeChallengeMethod,
|
||||
}
|
||||
|
||||
// generate token code
|
||||
code, err := s.AuthorizeTokenGen.GenerateAuthorizeToken(ret)
|
||||
if err != nil {
|
||||
w.SetErrorState(E_SERVER_ERROR, "", ar.State)
|
||||
w.InternalError = err
|
||||
return
|
||||
}
|
||||
ret.Code = code
|
||||
|
||||
// save authorization token
|
||||
if err = w.Storage.SaveAuthorize(ret); err != nil {
|
||||
w.SetErrorState(E_SERVER_ERROR, "", ar.State)
|
||||
w.InternalError = err
|
||||
return
|
||||
}
|
||||
|
||||
// redirect with code
|
||||
w.Output["code"] = ret.Code
|
||||
w.Output["state"] = ret.State
|
||||
}
|
||||
} else {
|
||||
// redirect with error
|
||||
w.SetErrorState(E_ACCESS_DENIED, "", ar.State)
|
||||
}
|
||||
}
|
296
vendor/github.com/RangelReale/osin/authorize_test.go
generated
vendored
Normal file
296
vendor/github.com/RangelReale/osin/authorize_test.go
generated
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthorizeCode(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(CODE))
|
||||
req.Form.Set("client_id", "1234")
|
||||
req.Form.Set("state", "a")
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != REDIRECT {
|
||||
t.Fatalf("Response should be a redirect")
|
||||
}
|
||||
|
||||
if d := resp.Output["code"]; d != "1" {
|
||||
t.Fatalf("Unexpected authorization code: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeToken(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{TOKEN}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(TOKEN))
|
||||
req.Form.Set("client_id", "1234")
|
||||
req.Form.Set("state", "a")
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != REDIRECT || !resp.RedirectInFragment {
|
||||
t.Fatalf("Response should be a redirect with fragment")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "1" {
|
||||
t.Fatalf("Unexpected access token: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeTokenWithInvalidClient(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{TOKEN}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
|
||||
server.AccessTokenGen = &TestingAccessTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
redirectUri := "http://redirecturi.com"
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(TOKEN))
|
||||
req.Form.Set("client_id", "invalidclient")
|
||||
req.Form.Set("state", "a")
|
||||
req.Form.Set("redirect_uri", redirectUri)
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
if !resp.IsError {
|
||||
t.Fatalf("Response should be an error")
|
||||
}
|
||||
|
||||
if resp.ErrorId != E_UNAUTHORIZED_CLIENT {
|
||||
t.Fatalf("Incorrect error in response: %v", resp.ErrorId)
|
||||
}
|
||||
|
||||
usedRedirectUrl, redirectErr := resp.GetRedirectUrl()
|
||||
|
||||
if redirectErr == nil && usedRedirectUrl == redirectUri {
|
||||
t.Fatalf("Response must not redirect to the provided redirect URL for an invalid client")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeCodePKCERequired(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.RequirePKCEForPublicClients = true
|
||||
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
|
||||
|
||||
// Public client returns an error
|
||||
{
|
||||
resp := server.NewResponse()
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(CODE))
|
||||
req.Form.Set("state", "a")
|
||||
req.Form.Set("client_id", "public-client")
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
if !resp.IsError || resp.ErrorId != "invalid_request" || strings.Contains(resp.StatusText, "code_challenge") {
|
||||
t.Errorf("Expected invalid_request error describing the code_challenge required, got %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
// Confidential client works without PKCE
|
||||
{
|
||||
resp := server.NewResponse()
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(CODE))
|
||||
req.Form.Set("state", "a")
|
||||
req.Form.Set("client_id", "1234")
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
if resp.Type != REDIRECT {
|
||||
t.Fatalf("Response should be a redirect")
|
||||
}
|
||||
if d := resp.Output["code"]; d != "1" {
|
||||
t.Fatalf("Unexpected authorization code: %s", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeCodePKCEPlain(t *testing.T) {
|
||||
challenge := "12345678901234567890123456789012345678901234567890"
|
||||
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(CODE))
|
||||
req.Form.Set("client_id", "1234")
|
||||
req.Form.Set("state", "a")
|
||||
req.Form.Set("code_challenge", challenge)
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != REDIRECT {
|
||||
t.Fatalf("Response should be a redirect")
|
||||
}
|
||||
|
||||
code, ok := resp.Output["code"].(string)
|
||||
if !ok || code != "1" {
|
||||
t.Fatalf("Unexpected authorization code: %s", code)
|
||||
}
|
||||
|
||||
token, err := server.Storage.LoadAuthorize(code)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
if token.CodeChallenge != challenge {
|
||||
t.Errorf("Expected stored code_challenge %s, got %s", challenge, token.CodeChallenge)
|
||||
}
|
||||
if token.CodeChallengeMethod != "plain" {
|
||||
t.Errorf("Expected stored code_challenge plain, got %s", token.CodeChallengeMethod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeCodePKCES256(t *testing.T) {
|
||||
challenge := "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
|
||||
|
||||
sconfig := NewServerConfig()
|
||||
sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE}
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{}
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("response_type", string(CODE))
|
||||
req.Form.Set("client_id", "1234")
|
||||
req.Form.Set("state", "a")
|
||||
req.Form.Set("code_challenge", challenge)
|
||||
req.Form.Set("code_challenge_method", "S256")
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", resp)
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != REDIRECT {
|
||||
t.Fatalf("Response should be a redirect")
|
||||
}
|
||||
|
||||
code, ok := resp.Output["code"].(string)
|
||||
if !ok || code != "1" {
|
||||
t.Fatalf("Unexpected authorization code: %s", code)
|
||||
}
|
||||
|
||||
token, err := server.Storage.LoadAuthorize(code)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
if token.CodeChallenge != challenge {
|
||||
t.Errorf("Expected stored code_challenge %s, got %s", challenge, token.CodeChallenge)
|
||||
}
|
||||
if token.CodeChallengeMethod != "S256" {
|
||||
t.Errorf("Expected stored code_challenge S256, got %s", token.CodeChallengeMethod)
|
||||
}
|
||||
}
|
60
vendor/github.com/RangelReale/osin/client.go
generated
vendored
Normal file
60
vendor/github.com/RangelReale/osin/client.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package osin
|
||||
|
||||
// Client information
|
||||
type Client interface {
|
||||
// Client id
|
||||
GetId() string
|
||||
|
||||
// Client secret
|
||||
GetSecret() string
|
||||
|
||||
// Base client uri
|
||||
GetRedirectUri() string
|
||||
|
||||
// Data to be passed to storage. Not used by the library.
|
||||
GetUserData() interface{}
|
||||
}
|
||||
|
||||
// ClientSecretMatcher is an optional interface clients can implement
|
||||
// which allows them to be the one to determine if a secret matches.
|
||||
// If a Client implements ClientSecretMatcher, the framework will never call GetSecret
|
||||
type ClientSecretMatcher interface {
|
||||
// SecretMatches returns true if the given secret matches
|
||||
ClientSecretMatches(secret string) bool
|
||||
}
|
||||
|
||||
// DefaultClient stores all data in struct variables
|
||||
type DefaultClient struct {
|
||||
Id string
|
||||
Secret string
|
||||
RedirectUri string
|
||||
UserData interface{}
|
||||
}
|
||||
|
||||
func (d *DefaultClient) GetId() string {
|
||||
return d.Id
|
||||
}
|
||||
|
||||
func (d *DefaultClient) GetSecret() string {
|
||||
return d.Secret
|
||||
}
|
||||
|
||||
func (d *DefaultClient) GetRedirectUri() string {
|
||||
return d.RedirectUri
|
||||
}
|
||||
|
||||
func (d *DefaultClient) GetUserData() interface{} {
|
||||
return d.UserData
|
||||
}
|
||||
|
||||
// Implement the ClientSecretMatcher interface
|
||||
func (d *DefaultClient) ClientSecretMatches(secret string) bool {
|
||||
return d.Secret == secret
|
||||
}
|
||||
|
||||
func (d *DefaultClient) CopyFrom(client Client) {
|
||||
d.Id = client.GetId()
|
||||
d.Secret = client.GetSecret()
|
||||
d.RedirectUri = client.GetRedirectUri()
|
||||
d.UserData = client.GetUserData()
|
||||
}
|
18
vendor/github.com/RangelReale/osin/client_test.go
generated
vendored
Normal file
18
vendor/github.com/RangelReale/osin/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClientIntfUserData(t *testing.T) {
|
||||
c := &DefaultClient{
|
||||
UserData: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// check if the interface{} returned from the method is a reference
|
||||
c.GetUserData().(map[string]interface{})["test"] = "none"
|
||||
|
||||
if _, ok := c.GetUserData().(map[string]interface{})["test"]; !ok {
|
||||
t.Error("Returned interface is not a reference")
|
||||
}
|
||||
}
|
82
vendor/github.com/RangelReale/osin/config.go
generated
vendored
Normal file
82
vendor/github.com/RangelReale/osin/config.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package osin
|
||||
|
||||
// AllowedAuthorizeType is a collection of allowed auth request types
|
||||
type AllowedAuthorizeType []AuthorizeRequestType
|
||||
|
||||
// Exists returns true if the auth type exists in the list
|
||||
func (t AllowedAuthorizeType) Exists(rt AuthorizeRequestType) bool {
|
||||
for _, k := range t {
|
||||
if k == rt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowedAccessType is a collection of allowed access request types
|
||||
type AllowedAccessType []AccessRequestType
|
||||
|
||||
// Exists returns true if the access type exists in the list
|
||||
func (t AllowedAccessType) Exists(rt AccessRequestType) bool {
|
||||
for _, k := range t {
|
||||
if k == rt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ServerConfig contains server configuration information
|
||||
type ServerConfig struct {
|
||||
// Authorization token expiration in seconds (default 5 minutes)
|
||||
AuthorizationExpiration int32
|
||||
|
||||
// Access token expiration in seconds (default 1 hour)
|
||||
AccessExpiration int32
|
||||
|
||||
// Token type to return
|
||||
TokenType string
|
||||
|
||||
// List of allowed authorize types (only CODE by default)
|
||||
AllowedAuthorizeTypes AllowedAuthorizeType
|
||||
|
||||
// List of allowed access types (only AUTHORIZATION_CODE by default)
|
||||
AllowedAccessTypes AllowedAccessType
|
||||
|
||||
// HTTP status code to return for errors - default 200
|
||||
// Only used if response was created from server
|
||||
ErrorStatusCode int
|
||||
|
||||
// If true allows client secret also in params, else only in
|
||||
// Authorization header - default false
|
||||
AllowClientSecretInParams bool
|
||||
|
||||
// If true allows access request using GET, else only POST - default false
|
||||
AllowGetAccessRequest bool
|
||||
|
||||
// Require PKCE for code flows for public OAuth clients - default false
|
||||
RequirePKCEForPublicClients bool
|
||||
|
||||
// Separator to support multiple URIs in Client.GetRedirectUri().
|
||||
// If blank (the default), don't allow multiple URIs.
|
||||
RedirectUriSeparator string
|
||||
|
||||
// RetainTokenAfter Refresh allows the server to retain the access and
|
||||
// refresh token for re-use - default false
|
||||
RetainTokenAfterRefresh bool
|
||||
}
|
||||
|
||||
// NewServerConfig returns a new ServerConfig with default configuration
|
||||
func NewServerConfig() *ServerConfig {
|
||||
return &ServerConfig{
|
||||
AuthorizationExpiration: 250,
|
||||
AccessExpiration: 3600,
|
||||
TokenType: "Bearer",
|
||||
AllowedAuthorizeTypes: AllowedAuthorizeType{CODE},
|
||||
AllowedAccessTypes: AllowedAccessType{AUTHORIZATION_CODE},
|
||||
ErrorStatusCode: 200,
|
||||
AllowClientSecretInParams: false,
|
||||
AllowGetAccessRequest: false,
|
||||
RetainTokenAfterRefresh: false,
|
||||
}
|
||||
}
|
52
vendor/github.com/RangelReale/osin/error.go
generated
vendored
Normal file
52
vendor/github.com/RangelReale/osin/error.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package osin
|
||||
|
||||
type DefaultErrorId string
|
||||
|
||||
const (
|
||||
E_INVALID_REQUEST string = "invalid_request"
|
||||
E_UNAUTHORIZED_CLIENT = "unauthorized_client"
|
||||
E_ACCESS_DENIED = "access_denied"
|
||||
E_UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type"
|
||||
E_INVALID_SCOPE = "invalid_scope"
|
||||
E_SERVER_ERROR = "server_error"
|
||||
E_TEMPORARILY_UNAVAILABLE = "temporarily_unavailable"
|
||||
E_UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type"
|
||||
E_INVALID_GRANT = "invalid_grant"
|
||||
E_INVALID_CLIENT = "invalid_client"
|
||||
)
|
||||
|
||||
var (
|
||||
deferror *DefaultErrors = NewDefaultErrors()
|
||||
)
|
||||
|
||||
// Default errors and messages
|
||||
type DefaultErrors struct {
|
||||
errormap map[string]string
|
||||
}
|
||||
|
||||
// NewDefaultErrors initializes OAuth2 error codes and descriptions.
|
||||
// http://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
// http://tools.ietf.org/html/rfc6749#section-4.2.2.1
|
||||
// http://tools.ietf.org/html/rfc6749#section-5.2
|
||||
// http://tools.ietf.org/html/rfc6749#section-7.2
|
||||
func NewDefaultErrors() *DefaultErrors {
|
||||
r := &DefaultErrors{errormap: make(map[string]string)}
|
||||
r.errormap[E_INVALID_REQUEST] = "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed."
|
||||
r.errormap[E_UNAUTHORIZED_CLIENT] = "The client is not authorized to request a token using this method."
|
||||
r.errormap[E_ACCESS_DENIED] = "The resource owner or authorization server denied the request."
|
||||
r.errormap[E_UNSUPPORTED_RESPONSE_TYPE] = "The authorization server does not support obtaining a token using this method."
|
||||
r.errormap[E_INVALID_SCOPE] = "The requested scope is invalid, unknown, or malformed."
|
||||
r.errormap[E_SERVER_ERROR] = "The authorization server encountered an unexpected condition that prevented it from fulfilling the request."
|
||||
r.errormap[E_TEMPORARILY_UNAVAILABLE] = "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server."
|
||||
r.errormap[E_UNSUPPORTED_GRANT_TYPE] = "The authorization grant type is not supported by the authorization server."
|
||||
r.errormap[E_INVALID_GRANT] = "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
|
||||
r.errormap[E_INVALID_CLIENT] = "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)."
|
||||
return r
|
||||
}
|
||||
|
||||
func (e *DefaultErrors) Get(id string) string {
|
||||
if m, ok := e.errormap[id]; ok {
|
||||
return m
|
||||
}
|
||||
return id
|
||||
}
|
411
vendor/github.com/RangelReale/osin/example/complete/complete.go
generated
vendored
Normal file
411
vendor/github.com/RangelReale/osin/example/complete/complete.go
generated
vendored
Normal file
@@ -0,0 +1,411 @@
|
||||
package main
|
||||
|
||||
// Open url in browser:
|
||||
// http://localhost:14000/app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sconfig := osin.NewServerConfig()
|
||||
sconfig.AllowedAuthorizeTypes = osin.AllowedAuthorizeType{osin.CODE, osin.TOKEN}
|
||||
sconfig.AllowedAccessTypes = osin.AllowedAccessType{osin.AUTHORIZATION_CODE,
|
||||
osin.REFRESH_TOKEN, osin.PASSWORD, osin.CLIENT_CREDENTIALS, osin.ASSERTION}
|
||||
sconfig.AllowGetAccessRequest = true
|
||||
sconfig.AllowClientSecretInParams = true
|
||||
server := osin.NewServer(sconfig, example.NewTestStorage())
|
||||
|
||||
// Authorization code endpoint
|
||||
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
ar.UserData = struct{ Login string }{Login: "test"}
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
if !resp.IsError {
|
||||
resp.Output["custom_parameter"] = 187723
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
switch ar.Type {
|
||||
case osin.AUTHORIZATION_CODE:
|
||||
ar.Authorized = true
|
||||
case osin.REFRESH_TOKEN:
|
||||
ar.Authorized = true
|
||||
case osin.PASSWORD:
|
||||
if ar.Username == "test" && ar.Password == "test" {
|
||||
ar.Authorized = true
|
||||
}
|
||||
case osin.CLIENT_CREDENTIALS:
|
||||
ar.Authorized = true
|
||||
case osin.ASSERTION:
|
||||
if ar.AssertionType == "urn:osin.example.complete" && ar.Assertion == "osin.data" {
|
||||
ar.Authorized = true
|
||||
}
|
||||
}
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
if !resp.IsError {
|
||||
resp.Output["custom_parameter"] = 19923
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Information endpoint
|
||||
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ir := server.HandleInfoRequest(resp, r); ir != nil {
|
||||
server.FinishInfoRequest(resp, r, ir)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Application home endpoint
|
||||
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("<html><body>"))
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Code</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=token&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Implicit</a><br/>", url.QueryEscape("http://localhost:14000/appauth/token"))))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/appauth/password\">Password</a><br/>")))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/appauth/client_credentials\">Client Credentials</a><br/>")))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/appauth/assertion\">Assertion</a><br/>")))
|
||||
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - CODE
|
||||
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - CODE<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&client_secret=aabbccdd&state=xyz&redirect_uri=%s&code=%s",
|
||||
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
|
||||
|
||||
// if parse, download and parse json
|
||||
if r.Form.Get("doparse") == "1" {
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
// output links
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
|
||||
|
||||
cururl := *r.URL
|
||||
curq := cururl.Query()
|
||||
curq.Add("doparse", "1")
|
||||
cururl.RawQuery = curq.Encode()
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
|
||||
|
||||
if rt, ok := jr["refresh_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
|
||||
}
|
||||
})
|
||||
|
||||
// Application destination - TOKEN
|
||||
http.HandleFunc("/appauth/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - TOKEN<br/>"))
|
||||
|
||||
w.Write([]byte("Response data in fragment - not acessible via server - Nothing to do"))
|
||||
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - PASSWORD
|
||||
http.HandleFunc("/appauth/password", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - PASSWORD<br/>"))
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=password&scope=everything&username=%s&password=%s",
|
||||
"test", "test")
|
||||
|
||||
// download token
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
if rt, ok := jr["refresh_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - CLIENT_CREDENTIALS
|
||||
http.HandleFunc("/appauth/client_credentials", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - CLIENT CREDENTIALS<br/>"))
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=client_credentials")
|
||||
|
||||
// download token
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
if rt, ok := jr["refresh_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - ASSERTION
|
||||
http.HandleFunc("/appauth/assertion", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - ASSERTION<br/>"))
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=assertion&assertion_type=urn:osin.example.complete&assertion=osin.data")
|
||||
|
||||
// download token
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
if rt, ok := jr["refresh_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - REFRESH
|
||||
http.HandleFunc("/appauth/refresh", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - REFRESH<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=refresh_token&refresh_token=%s", url.QueryEscape(code))
|
||||
|
||||
// download token
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
if rt, ok := jr["refresh_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
|
||||
}
|
||||
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/info?code=%s", at)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Info</a><br/>", rurl)))
|
||||
}
|
||||
})
|
||||
|
||||
// Application destination - INFO
|
||||
http.HandleFunc("/appauth/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - INFO<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/info?code=%s", url.QueryEscape(code))
|
||||
|
||||
// download token
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{Username: "1234", Password: "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
if rt, ok := jr["refresh_token"]; ok {
|
||||
rurl := fmt.Sprintf("/appauth/refresh?code=%s", rt)
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Refresh Token</a><br/>", rurl)))
|
||||
}
|
||||
})
|
||||
|
||||
http.ListenAndServe(":14000", nil)
|
||||
}
|
130
vendor/github.com/RangelReale/osin/example/goauth2client/goauth2client.go
generated
vendored
Normal file
130
vendor/github.com/RangelReale/osin/example/goauth2client/goauth2client.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
// Use golang.org/x/oauth2 client to test
|
||||
// Open url in browser:
|
||||
// http://localhost:14000/app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := osin.NewServerConfig()
|
||||
// goauth2 checks errors using status codes
|
||||
config.ErrorStatusCode = 401
|
||||
|
||||
server := osin.NewServer(config, example.NewTestStorage())
|
||||
|
||||
client := &oauth2.Config{
|
||||
ClientID: "1234",
|
||||
ClientSecret: "aabbccdd",
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "http://localhost:14000/authorize",
|
||||
TokenURL: "http://localhost:14000/token",
|
||||
},
|
||||
RedirectURL: "http://localhost:14000/appauth/code",
|
||||
}
|
||||
|
||||
// Authorization code endpoint
|
||||
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Information endpoint
|
||||
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ir := server.HandleInfoRequest(resp, r); ir != nil {
|
||||
server.FinishInfoRequest(resp, r, ir)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Application home endpoint
|
||||
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("<html><body>"))
|
||||
//w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a><br/>", client.AuthCodeURL(""))))
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - CODE
|
||||
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - CODE<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
var jr *oauth2.Token
|
||||
var err error
|
||||
|
||||
// if parse, download and parse json
|
||||
if r.Form.Get("doparse") == "1" {
|
||||
jr, err = client.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
jr = nil
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", err)))
|
||||
}
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if jr != nil {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", jr.AccessToken)))
|
||||
if jr.RefreshToken != "" {
|
||||
w.Write([]byte(fmt.Sprintf("REFRESH TOKEN: %s<br/>\n", jr.RefreshToken)))
|
||||
}
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
cururl := *r.URL
|
||||
curq := cururl.Query()
|
||||
curq.Add("doparse", "1")
|
||||
cururl.RawQuery = curq.Encode()
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":14000", nil)
|
||||
}
|
57
vendor/github.com/RangelReale/osin/example/helper.go
generated
vendored
Normal file
57
vendor/github.com/RangelReale/osin/example/helper.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
)
|
||||
|
||||
func HandleLoginPage(ar *osin.AuthorizeRequest, w http.ResponseWriter, r *http.Request) bool {
|
||||
r.ParseForm()
|
||||
if r.Method == "POST" && r.Form.Get("login") == "test" && r.Form.Get("password") == "test" {
|
||||
return true
|
||||
}
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("LOGIN %s (use test/test)<br/>", ar.Client.GetId())))
|
||||
w.Write([]byte(fmt.Sprintf("<form action=\"/authorize?%s\" method=\"POST\">", r.URL.RawQuery)))
|
||||
|
||||
w.Write([]byte("Login: <input type=\"text\" name=\"login\" /><br/>"))
|
||||
w.Write([]byte("Password: <input type=\"password\" name=\"password\" /><br/>"))
|
||||
w.Write([]byte("<input type=\"submit\"/>"))
|
||||
|
||||
w.Write([]byte("</form>"))
|
||||
|
||||
w.Write([]byte("</body></html>"))
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func DownloadAccessToken(url string, auth *osin.BasicAuth, output map[string]interface{}) error {
|
||||
// download access token
|
||||
preq, err := http.NewRequest("POST", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if auth != nil {
|
||||
preq.SetBasicAuth(auth.Username, auth.Password)
|
||||
}
|
||||
|
||||
pclient := &http.Client{}
|
||||
presp, err := pclient.Do(preq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if presp.StatusCode != 200 {
|
||||
return errors.New("Invalid status code")
|
||||
}
|
||||
|
||||
jdec := json.NewDecoder(presp.Body)
|
||||
err = jdec.Decode(&output)
|
||||
return err
|
||||
}
|
215
vendor/github.com/RangelReale/osin/example/jwttoken/jwttoken.go
generated
vendored
Normal file
215
vendor/github.com/RangelReale/osin/example/jwttoken/jwttoken.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
package main
|
||||
|
||||
// Open url in browser:
|
||||
// http://localhost:14000/app
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// JWT access token generator
|
||||
type AccessTokenGenJWT struct {
|
||||
PrivateKey *rsa.PrivateKey
|
||||
PublicKey *rsa.PublicKey
|
||||
}
|
||||
|
||||
func (c *AccessTokenGenJWT) GenerateAccessToken(data *osin.AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
|
||||
// generate JWT access token
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
|
||||
"cid": data.Client.GetId(),
|
||||
"exp": data.ExpireAt().Unix(),
|
||||
})
|
||||
|
||||
accesstoken, err = token.SignedString(c.PrivateKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if !generaterefresh {
|
||||
return
|
||||
}
|
||||
|
||||
// generate JWT refresh token
|
||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
|
||||
"cid": data.Client.GetId(),
|
||||
})
|
||||
|
||||
refreshtoken, err = token.SignedString(c.PrivateKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
server := osin.NewServer(osin.NewServerConfig(), example.NewTestStorage())
|
||||
|
||||
var err error
|
||||
var accessTokenGenJWT AccessTokenGenJWT
|
||||
|
||||
if accessTokenGenJWT.PrivateKey, err = jwt.ParseRSAPrivateKeyFromPEM(privatekeyPEM); err != nil {
|
||||
fmt.Printf("ERROR: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if accessTokenGenJWT.PublicKey, err = jwt.ParseRSAPublicKeyFromPEM(publickeyPEM); err != nil {
|
||||
fmt.Printf("ERROR: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
server.AccessTokenGen = &accessTokenGenJWT
|
||||
|
||||
// Authorization code endpoint
|
||||
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Information endpoint
|
||||
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ir := server.HandleInfoRequest(resp, r); ir != nil {
|
||||
server.FinishInfoRequest(resp, r, ir)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Application home endpoint
|
||||
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - CODE
|
||||
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - CODE<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&state=xyz&redirect_uri=%s&code=%s",
|
||||
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
|
||||
|
||||
// if parse, download and parse json
|
||||
if r.Form.Get("doparse") == "1" {
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
// output links
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
|
||||
|
||||
cururl := *r.URL
|
||||
curq := cururl.Query()
|
||||
curq.Add("doparse", "1")
|
||||
cururl.RawQuery = curq.Encode()
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":14000", nil)
|
||||
}
|
||||
|
||||
var (
|
||||
privatekeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn
|
||||
SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i
|
||||
cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC
|
||||
PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR
|
||||
ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA
|
||||
Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3
|
||||
n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy
|
||||
MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9
|
||||
POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE
|
||||
KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM
|
||||
IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn
|
||||
FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY
|
||||
mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj
|
||||
FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U
|
||||
I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs
|
||||
2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn
|
||||
/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT
|
||||
OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86
|
||||
EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+
|
||||
hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0
|
||||
4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb
|
||||
mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry
|
||||
eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3
|
||||
CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+
|
||||
9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
publickeyPEM = []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41
|
||||
fGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7
|
||||
mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp
|
||||
HssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2
|
||||
XrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b
|
||||
ODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy
|
||||
7wIDAQAB
|
||||
-----END PUBLIC KEY-----`)
|
||||
)
|
273
vendor/github.com/RangelReale/osin/example/openidconnect/openidconnect.go
generated
vendored
Normal file
273
vendor/github.com/RangelReale/osin/example/openidconnect/openidconnect.go
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
An example of adding OpenID Connect support to osin.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
|
||||
"gopkg.in/square/go-jose.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
issuer = "http://127.0.0.1:14001"
|
||||
server = osin.NewServer(osin.NewServerConfig(), example.NewTestStorage())
|
||||
|
||||
jwtSigner jose.Signer
|
||||
publicKeys *jose.JsonWebKeySet
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load signing key.
|
||||
block, _ := pem.Decode(privateKeyBytes)
|
||||
if block == nil {
|
||||
log.Fatalf("no private key found")
|
||||
}
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse key: %v", err)
|
||||
}
|
||||
|
||||
// Configure jwtSigner and public keys.
|
||||
privateKey := &jose.JsonWebKey{
|
||||
Key: key,
|
||||
Algorithm: "RS256",
|
||||
Use: "sig",
|
||||
KeyID: "1", // KeyID should use the key thumbprint.
|
||||
}
|
||||
|
||||
jwtSigner, err = jose.NewSigner(jose.RS256, privateKey)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create jwtSigner: %v", err)
|
||||
}
|
||||
publicKeys = &jose.JsonWebKeySet{
|
||||
Keys: []jose.JsonWebKey{
|
||||
jose.JsonWebKey{Key: &key.PublicKey,
|
||||
Algorithm: "RS256",
|
||||
Use: "sig",
|
||||
KeyID: "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Register the four manditory OpenID Connect endpoints: discovery, public keys, auth, and token.
|
||||
http.HandleFunc("/.well-known/openid-configuration", handleDiscovery)
|
||||
http.HandleFunc("/publickeys", handlePublicKeys)
|
||||
http.HandleFunc("/authorize", handleAuthorization)
|
||||
http.HandleFunc("/token", handleToken)
|
||||
|
||||
log.Fatal(http.ListenAndServe("127.0.0.1:14001", nil))
|
||||
}
|
||||
|
||||
// The ID Token represents a JWT passed to the client as part of the token response.
|
||||
//
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
type IDToken struct {
|
||||
Issuer string `json:"iss"`
|
||||
UserID string `json:"sub"`
|
||||
ClientID string `json:"aud"`
|
||||
Expiration int64 `json:"exp"`
|
||||
IssuedAt int64 `json:"iat"`
|
||||
|
||||
Nonce string `json:"nonce,omitempty"` // Non-manditory fields MUST be "omitempty"
|
||||
|
||||
// Custom claims supported by this server.
|
||||
//
|
||||
// See: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
|
||||
|
||||
Email string `json:"email,omitempty"`
|
||||
EmailVerified *bool `json:"email_verified,omitempty"`
|
||||
|
||||
Name string `json:"name,omitempty"`
|
||||
FamilyName string `json:"family_name,omitempty"`
|
||||
GivenName string `json:"given_name,omitempty"`
|
||||
Locale string `json:"locale,omitempty"`
|
||||
}
|
||||
|
||||
// handleDiscovery returns the OpenID Connect discovery object, allowing clients
|
||||
// to discover OAuth2 resources.
|
||||
func handleDiscovery(w http.ResponseWriter, r *http.Request) {
|
||||
// For other example see: https://accounts.google.com/.well-known/openid-configuration
|
||||
data := map[string]interface{}{
|
||||
"issuer": issuer,
|
||||
"authorization_endpoint": issuer + "/authorize",
|
||||
"token_endpoint": issuer + "/token",
|
||||
"jwks_uri": issuer + "/publickeys",
|
||||
"response_types_supported": []string{"code"},
|
||||
"subject_types_supported": []string{"public"},
|
||||
"id_token_signing_alg_values_supported": []string{"RS256"},
|
||||
"scopes_supported": []string{"openid", "email", "profile"},
|
||||
"token_endpoint_auth_methods_supported": []string{"client_secret_basic"},
|
||||
"claims_supported": []string{
|
||||
"aud", "email", "email_verified", "exp",
|
||||
"family_name", "given_name", "iat", "iss",
|
||||
"locale", "name", "sub",
|
||||
},
|
||||
}
|
||||
|
||||
raw, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
log.Printf("failed to marshal data: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(raw)))
|
||||
w.Write(raw)
|
||||
}
|
||||
|
||||
// handlePublicKeys publishes the public part of this server's signing keys.
|
||||
// This allows clients to verify the signature of ID Tokens.
|
||||
func handlePublicKeys(w http.ResponseWriter, r *http.Request) {
|
||||
raw, err := json.MarshalIndent(publicKeys, "", " ")
|
||||
if err != nil {
|
||||
log.Printf("failed to marshal data: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(raw)))
|
||||
w.Write(raw)
|
||||
}
|
||||
|
||||
func handleAuthorization(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
ar.Authorized = true
|
||||
scopes := make(map[string]bool)
|
||||
for _, s := range strings.Fields(ar.Scope) {
|
||||
scopes[s] = true
|
||||
}
|
||||
|
||||
// If the "openid" connect scope is specified, attach an ID Token to the
|
||||
// authorization response.
|
||||
//
|
||||
// The ID Token will be serialized and signed during the code for token exchange.
|
||||
if scopes["openid"] {
|
||||
|
||||
// These values would be tied to the end user authorizing the client.
|
||||
now := time.Now()
|
||||
idToken := IDToken{
|
||||
Issuer: issuer,
|
||||
UserID: "id-of-test-user",
|
||||
ClientID: ar.Client.GetId(),
|
||||
Expiration: now.Add(time.Hour).Unix(),
|
||||
IssuedAt: now.Unix(),
|
||||
Nonce: r.URL.Query().Get("nonce"),
|
||||
}
|
||||
|
||||
if scopes["profile"] {
|
||||
idToken.Name = "Jane Doe"
|
||||
idToken.GivenName = "Jane"
|
||||
idToken.FamilyName = "Doe"
|
||||
idToken.Locale = "us"
|
||||
}
|
||||
|
||||
if scopes["email"] {
|
||||
t := true
|
||||
idToken.Email = "jane.doe@example.com"
|
||||
idToken.EmailVerified = &t
|
||||
}
|
||||
// NOTE: The storage must be able to encode and decode this object.
|
||||
ar.UserData = &idToken
|
||||
}
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
log.Printf("internal error: %v", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
}
|
||||
|
||||
func handleToken(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
|
||||
// If an ID Token was encoded as the UserData, serialize and sign it.
|
||||
if idToken, ok := ar.UserData.(*IDToken); ok && idToken != nil {
|
||||
encodeIDToken(resp, idToken, jwtSigner)
|
||||
}
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
}
|
||||
|
||||
// encodeIDToken serializes and signs an ID Token then adds a field to the token response.
|
||||
func encodeIDToken(resp *osin.Response, idToken *IDToken, singer jose.Signer) {
|
||||
resp.InternalError = func() error {
|
||||
payload, err := json.Marshal(idToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal token: %v", err)
|
||||
}
|
||||
jws, err := jwtSigner.Sign(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sign token: %v", err)
|
||||
}
|
||||
raw, err := jws.CompactSerialize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize token: %v", err)
|
||||
}
|
||||
resp.Output["id_token"] = raw
|
||||
return nil
|
||||
}()
|
||||
|
||||
// Record errors as internal server errors.
|
||||
if resp.InternalError != nil {
|
||||
resp.IsError = true
|
||||
resp.ErrorId = osin.E_SERVER_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
privateKeyBytes = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn
|
||||
SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i
|
||||
cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC
|
||||
PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR
|
||||
ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA
|
||||
Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3
|
||||
n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy
|
||||
MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9
|
||||
POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE
|
||||
KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM
|
||||
IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn
|
||||
FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY
|
||||
mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj
|
||||
FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U
|
||||
I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs
|
||||
2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn
|
||||
/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT
|
||||
OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86
|
||||
EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+
|
||||
hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0
|
||||
4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb
|
||||
mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry
|
||||
eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3
|
||||
CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+
|
||||
9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
127
vendor/github.com/RangelReale/osin/example/osincliclient/osincliclient.go
generated
vendored
Normal file
127
vendor/github.com/RangelReale/osin/example/osincliclient/osincliclient.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
// Use github.com/RangelReale/osincli client to test
|
||||
// Open url in browser:
|
||||
// http://localhost:14001
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
"github.com/RangelReale/osincli"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// create http muxes
|
||||
serverhttp := http.NewServeMux()
|
||||
clienthttp := http.NewServeMux()
|
||||
|
||||
// create server
|
||||
config := osin.NewServerConfig()
|
||||
sstorage := example.NewTestStorage()
|
||||
sstorage.SetClient("1234", &osin.DefaultClient{
|
||||
Id: "1234",
|
||||
Secret: "aabbccdd",
|
||||
RedirectUri: "http://localhost:14001/appauth",
|
||||
})
|
||||
server := osin.NewServer(config, sstorage)
|
||||
|
||||
// create client
|
||||
cliconfig := &osincli.ClientConfig{
|
||||
ClientId: "1234",
|
||||
ClientSecret: "aabbccdd",
|
||||
AuthorizeUrl: "http://localhost:14000/authorize",
|
||||
TokenUrl: "http://localhost:14000/token",
|
||||
RedirectUrl: "http://localhost:14001/appauth",
|
||||
}
|
||||
client, err := osincli.NewClient(cliconfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create a new request to generate the url
|
||||
areq := client.NewAuthorizeRequest(osincli.CODE)
|
||||
|
||||
// SERVER
|
||||
|
||||
// Authorization code endpoint
|
||||
serverhttp.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
serverhttp.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Information endpoint
|
||||
serverhttp.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ir := server.HandleInfoRequest(resp, r); ir != nil {
|
||||
server.FinishInfoRequest(resp, r, ir)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// CLIENT
|
||||
|
||||
// Home
|
||||
clienthttp.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
u := areq.GetAuthorizeUrl()
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a>", u.String())))
|
||||
})
|
||||
|
||||
// Auth endpoint
|
||||
clienthttp.HandleFunc("/appauth", func(w http.ResponseWriter, r *http.Request) {
|
||||
// parse a token request
|
||||
areqdata, err := areq.HandleRequest(r)
|
||||
if err != nil {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err)))
|
||||
return
|
||||
}
|
||||
|
||||
treq := client.NewAccessRequest(osincli.AUTHORIZATION_CODE, areqdata)
|
||||
|
||||
// show access request url (for debugging only)
|
||||
u2 := treq.GetTokenUrl()
|
||||
w.Write([]byte(fmt.Sprintf("Access token URL: %s\n", u2.String())))
|
||||
|
||||
// exchange the authorize token for the access token
|
||||
ad, err := treq.GetToken()
|
||||
if err != nil {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err)))
|
||||
return
|
||||
}
|
||||
w.Write([]byte(fmt.Sprintf("Access token: %+v\n", ad)))
|
||||
})
|
||||
|
||||
go http.ListenAndServe(":14001", clienthttp)
|
||||
http.ListenAndServe(":14000", serverhttp)
|
||||
}
|
126
vendor/github.com/RangelReale/osin/example/simple/simple.go
generated
vendored
Normal file
126
vendor/github.com/RangelReale/osin/example/simple/simple.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
// Open url in browser:
|
||||
// http://localhost:14000/app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := osin.NewServerConfig()
|
||||
cfg.AllowGetAccessRequest = true
|
||||
cfg.AllowClientSecretInParams = true
|
||||
|
||||
server := osin.NewServer(cfg, example.NewTestStorage())
|
||||
|
||||
// Authorization code endpoint
|
||||
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Information endpoint
|
||||
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ir := server.HandleInfoRequest(resp, r); ir != nil {
|
||||
server.FinishInfoRequest(resp, r, ir)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Application home endpoint
|
||||
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - CODE
|
||||
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - CODE<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&client_secret=aabbccdd&state=xyz&redirect_uri=%s&code=%s",
|
||||
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
|
||||
|
||||
// if parse, download and parse json
|
||||
if r.Form.Get("doparse") == "1" {
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
// output links
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
|
||||
|
||||
cururl := *r.URL
|
||||
curq := cururl.Query()
|
||||
curq.Add("doparse", "1")
|
||||
cururl.RawQuery = curq.Encode()
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":14000", nil)
|
||||
}
|
108
vendor/github.com/RangelReale/osin/example/teststorage.go
generated
vendored
Normal file
108
vendor/github.com/RangelReale/osin/example/teststorage.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/RangelReale/osin"
|
||||
)
|
||||
|
||||
type TestStorage struct {
|
||||
clients map[string]osin.Client
|
||||
authorize map[string]*osin.AuthorizeData
|
||||
access map[string]*osin.AccessData
|
||||
refresh map[string]string
|
||||
}
|
||||
|
||||
func NewTestStorage() *TestStorage {
|
||||
r := &TestStorage{
|
||||
clients: make(map[string]osin.Client),
|
||||
authorize: make(map[string]*osin.AuthorizeData),
|
||||
access: make(map[string]*osin.AccessData),
|
||||
refresh: make(map[string]string),
|
||||
}
|
||||
|
||||
r.clients["1234"] = &osin.DefaultClient{
|
||||
Id: "1234",
|
||||
Secret: "aabbccdd",
|
||||
RedirectUri: "http://localhost:14000/appauth",
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *TestStorage) Clone() osin.Storage {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *TestStorage) Close() {
|
||||
}
|
||||
|
||||
func (s *TestStorage) GetClient(id string) (osin.Client, error) {
|
||||
fmt.Printf("GetClient: %s\n", id)
|
||||
if c, ok := s.clients[id]; ok {
|
||||
return c, nil
|
||||
}
|
||||
return nil, osin.ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestStorage) SetClient(id string, client osin.Client) error {
|
||||
fmt.Printf("SetClient: %s\n", id)
|
||||
s.clients[id] = client
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestStorage) SaveAuthorize(data *osin.AuthorizeData) error {
|
||||
fmt.Printf("SaveAuthorize: %s\n", data.Code)
|
||||
s.authorize[data.Code] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestStorage) LoadAuthorize(code string) (*osin.AuthorizeData, error) {
|
||||
fmt.Printf("LoadAuthorize: %s\n", code)
|
||||
if d, ok := s.authorize[code]; ok {
|
||||
return d, nil
|
||||
}
|
||||
return nil, osin.ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestStorage) RemoveAuthorize(code string) error {
|
||||
fmt.Printf("RemoveAuthorize: %s\n", code)
|
||||
delete(s.authorize, code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestStorage) SaveAccess(data *osin.AccessData) error {
|
||||
fmt.Printf("SaveAccess: %s\n", data.AccessToken)
|
||||
s.access[data.AccessToken] = data
|
||||
if data.RefreshToken != "" {
|
||||
s.refresh[data.RefreshToken] = data.AccessToken
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestStorage) LoadAccess(code string) (*osin.AccessData, error) {
|
||||
fmt.Printf("LoadAccess: %s\n", code)
|
||||
if d, ok := s.access[code]; ok {
|
||||
return d, nil
|
||||
}
|
||||
return nil, osin.ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestStorage) RemoveAccess(code string) error {
|
||||
fmt.Printf("RemoveAccess: %s\n", code)
|
||||
delete(s.access, code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestStorage) LoadRefresh(code string) (*osin.AccessData, error) {
|
||||
fmt.Printf("LoadRefresh: %s\n", code)
|
||||
if d, ok := s.refresh[code]; ok {
|
||||
return s.LoadAccess(d)
|
||||
}
|
||||
return nil, osin.ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestStorage) RemoveRefresh(code string) error {
|
||||
fmt.Printf("RemoveRefresh: %s\n", code)
|
||||
delete(s.refresh, code)
|
||||
return nil
|
||||
}
|
81
vendor/github.com/RangelReale/osin/info.go
generated
vendored
Normal file
81
vendor/github.com/RangelReale/osin/info.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// InfoRequest is a request for information about some AccessData
|
||||
type InfoRequest struct {
|
||||
Code string // Code to look up
|
||||
AccessData *AccessData // AccessData associated with Code
|
||||
}
|
||||
|
||||
// HandleInfoRequest is an http.HandlerFunc for server information
|
||||
// NOT an RFC specification.
|
||||
func (s *Server) HandleInfoRequest(w *Response, r *http.Request) *InfoRequest {
|
||||
r.ParseForm()
|
||||
bearer := CheckBearerAuth(r)
|
||||
if bearer == nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate info request
|
||||
ret := &InfoRequest{
|
||||
Code: bearer.Code,
|
||||
}
|
||||
|
||||
if ret.Code == "" {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// load access data
|
||||
ret.AccessData, err = w.Storage.LoadAccess(ret.Code)
|
||||
if err != nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData == nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData.Client == nil {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData.Client.GetRedirectUri() == "" {
|
||||
w.SetError(E_UNAUTHORIZED_CLIENT, "")
|
||||
return nil
|
||||
}
|
||||
if ret.AccessData.IsExpiredAt(s.Now()) {
|
||||
w.SetError(E_INVALID_GRANT, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// FinishInfoRequest finalizes the request handled by HandleInfoRequest
|
||||
func (s *Server) FinishInfoRequest(w *Response, r *http.Request, ir *InfoRequest) {
|
||||
// don't process if is already an error
|
||||
if w.IsError {
|
||||
return
|
||||
}
|
||||
|
||||
// output data
|
||||
w.Output["client_id"] = ir.AccessData.Client.GetId()
|
||||
w.Output["access_token"] = ir.AccessData.AccessToken
|
||||
w.Output["token_type"] = s.Config.TokenType
|
||||
w.Output["expires_in"] = ir.AccessData.CreatedAt.Add(time.Duration(ir.AccessData.ExpiresIn)*time.Second).Sub(s.Now()) / time.Second
|
||||
if ir.AccessData.RefreshToken != "" {
|
||||
w.Output["refresh_token"] = ir.AccessData.RefreshToken
|
||||
}
|
||||
if ir.AccessData.Scope != "" {
|
||||
w.Output["scope"] = ir.AccessData.Scope
|
||||
}
|
||||
}
|
72
vendor/github.com/RangelReale/osin/info_test.go
generated
vendored
Normal file
72
vendor/github.com/RangelReale/osin/info_test.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Form = make(url.Values)
|
||||
req.Form.Set("code", "9999")
|
||||
|
||||
if ar := server.HandleInfoRequest(resp, req); ar != nil {
|
||||
server.FinishInfoRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "9999" {
|
||||
t.Fatalf("Unexpected authorization code: %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfoWhenCodeIsOnHeader(t *testing.T) {
|
||||
sconfig := NewServerConfig()
|
||||
server := NewServer(sconfig, NewTestingStorage())
|
||||
resp := server.NewResponse()
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer 9999")
|
||||
|
||||
if ar := server.HandleInfoRequest(resp, req); ar != nil {
|
||||
server.FinishInfoRequest(resp, req, ar)
|
||||
}
|
||||
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
t.Fatalf("Error in response: %s", resp.InternalError)
|
||||
}
|
||||
|
||||
if resp.IsError {
|
||||
t.Fatalf("Should not be an error")
|
||||
}
|
||||
|
||||
if resp.Type != DATA {
|
||||
t.Fatalf("Response should be data")
|
||||
}
|
||||
|
||||
if d := resp.Output["access_token"]; d != "9999" {
|
||||
t.Fatalf("Unexpected authorization code: %s", d)
|
||||
}
|
||||
}
|
151
vendor/github.com/RangelReale/osin/response.go
generated
vendored
Normal file
151
vendor/github.com/RangelReale/osin/response.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Data for response output
|
||||
type ResponseData map[string]interface{}
|
||||
|
||||
// Response type enum
|
||||
type ResponseType int
|
||||
|
||||
const (
|
||||
DATA ResponseType = iota
|
||||
REDIRECT
|
||||
)
|
||||
|
||||
// Server response
|
||||
type Response struct {
|
||||
Type ResponseType
|
||||
StatusCode int
|
||||
StatusText string
|
||||
ErrorStatusCode int
|
||||
URL string
|
||||
Output ResponseData
|
||||
Headers http.Header
|
||||
IsError bool
|
||||
ErrorId string
|
||||
InternalError error
|
||||
RedirectInFragment bool
|
||||
|
||||
// Storage to use in this response - required
|
||||
Storage Storage
|
||||
}
|
||||
|
||||
func NewResponse(storage Storage) *Response {
|
||||
r := &Response{
|
||||
Type: DATA,
|
||||
StatusCode: 200,
|
||||
ErrorStatusCode: 200,
|
||||
Output: make(ResponseData),
|
||||
Headers: make(http.Header),
|
||||
IsError: false,
|
||||
Storage: storage.Clone(),
|
||||
}
|
||||
r.Headers.Add(
|
||||
"Cache-Control",
|
||||
"no-cache, no-store, max-age=0, must-revalidate",
|
||||
)
|
||||
r.Headers.Add("Pragma", "no-cache")
|
||||
r.Headers.Add("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
|
||||
return r
|
||||
}
|
||||
|
||||
// SetError sets an error id and description on the Response
|
||||
// state and uri are left blank
|
||||
func (r *Response) SetError(id string, description string) {
|
||||
r.SetErrorUri(id, description, "", "")
|
||||
}
|
||||
|
||||
// SetErrorState sets an error id, description, and state on the Response
|
||||
// uri is left blank
|
||||
func (r *Response) SetErrorState(id string, description string, state string) {
|
||||
r.SetErrorUri(id, description, "", state)
|
||||
}
|
||||
|
||||
// SetErrorUri sets an error id, description, state, and uri on the Response
|
||||
func (r *Response) SetErrorUri(id string, description string, uri string, state string) {
|
||||
// get default error message
|
||||
if description == "" {
|
||||
description = deferror.Get(id)
|
||||
}
|
||||
|
||||
// set error parameters
|
||||
r.IsError = true
|
||||
r.ErrorId = id
|
||||
r.StatusCode = r.ErrorStatusCode
|
||||
if r.StatusCode != 200 {
|
||||
r.StatusText = description
|
||||
} else {
|
||||
r.StatusText = ""
|
||||
}
|
||||
r.Output = make(ResponseData) // clear output
|
||||
r.Output["error"] = id
|
||||
r.Output["error_description"] = description
|
||||
if uri != "" {
|
||||
r.Output["error_uri"] = uri
|
||||
}
|
||||
if state != "" {
|
||||
r.Output["state"] = state
|
||||
}
|
||||
}
|
||||
|
||||
// SetRedirect changes the response to redirect to the given url
|
||||
func (r *Response) SetRedirect(url string) {
|
||||
// set redirect parameters
|
||||
r.Type = REDIRECT
|
||||
r.URL = url
|
||||
}
|
||||
|
||||
// SetRedirectFragment sets redirect values to be passed in fragment instead of as query parameters
|
||||
func (r *Response) SetRedirectFragment(f bool) {
|
||||
r.RedirectInFragment = f
|
||||
}
|
||||
|
||||
// GetRedirectUrl returns the redirect url with all query string parameters
|
||||
func (r *Response) GetRedirectUrl() (string, error) {
|
||||
if r.Type != REDIRECT {
|
||||
return "", errors.New("Not a redirect response")
|
||||
}
|
||||
|
||||
u, err := url.Parse(r.URL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var q url.Values
|
||||
if r.RedirectInFragment {
|
||||
// start with empty set for fragment
|
||||
q = url.Values{}
|
||||
} else {
|
||||
// add parameters to existing query
|
||||
q = u.Query()
|
||||
}
|
||||
|
||||
// add parameters
|
||||
for n, v := range r.Output {
|
||||
q.Set(n, fmt.Sprint(v))
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc6749#section-4.2.2
|
||||
// Fragment should be encoded as application/x-www-form-urlencoded (%-escaped, spaces are represented as '+')
|
||||
// The stdlib URL#String() doesn't make that easy to accomplish, so build this ourselves
|
||||
if r.RedirectInFragment {
|
||||
u.Fragment = ""
|
||||
redirectURI := u.String() + "#" + q.Encode()
|
||||
return redirectURI, nil
|
||||
}
|
||||
|
||||
// Otherwise, update the query and encode normally
|
||||
u.RawQuery = q.Encode()
|
||||
u.Fragment = ""
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (r *Response) Close() {
|
||||
r.Storage.Close()
|
||||
}
|
39
vendor/github.com/RangelReale/osin/response_json.go
generated
vendored
Normal file
39
vendor/github.com/RangelReale/osin/response_json.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// OutputJSON encodes the Response to JSON and writes to the http.ResponseWriter
|
||||
func OutputJSON(rs *Response, w http.ResponseWriter, r *http.Request) error {
|
||||
// Add headers
|
||||
for i, k := range rs.Headers {
|
||||
for _, v := range k {
|
||||
w.Header().Add(i, v)
|
||||
}
|
||||
}
|
||||
|
||||
if rs.Type == REDIRECT {
|
||||
// Output redirect with parameters
|
||||
u, err := rs.GetRedirectUrl()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Header().Add("Location", u)
|
||||
w.WriteHeader(302)
|
||||
} else {
|
||||
// set content type if the response doesn't already have one associated with it
|
||||
if w.Header().Get("Content-Type") == "" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
w.WriteHeader(rs.StatusCode)
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
err := encoder.Encode(rs.Output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
115
vendor/github.com/RangelReale/osin/response_json_test.go
generated
vendored
Normal file
115
vendor/github.com/RangelReale/osin/response_json_test.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResponseJSON(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r := NewResponse(NewTestingStorage())
|
||||
r.Output["access_token"] = "1234"
|
||||
r.Output["token_type"] = "5678"
|
||||
|
||||
err = OutputJSON(r, w, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Error outputting json: %s", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("%d - %s - %+v", w.Code, w.Body.String(), w.HeaderMap)
|
||||
|
||||
if w.Code != 200 {
|
||||
t.Fatalf("Invalid response code for output: %d", w.Code)
|
||||
}
|
||||
|
||||
if w.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("Result from json must be application/json")
|
||||
}
|
||||
|
||||
// parse output json
|
||||
output := make(map[string]interface{})
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &output); err != nil {
|
||||
t.Fatalf("Could not decode output json: %s", err)
|
||||
}
|
||||
|
||||
if d, ok := output["access_token"]; !ok || d != "1234" {
|
||||
t.Fatalf("Invalid or not found output data: access_token=%s", d)
|
||||
}
|
||||
|
||||
if d, ok := output["token_type"]; !ok || d != "5678" {
|
||||
t.Fatalf("Invalid or not found output data: token_type=%s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorResponseJSON(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r := NewResponse(NewTestingStorage())
|
||||
r.ErrorStatusCode = 500
|
||||
r.SetError(E_INVALID_REQUEST, "")
|
||||
|
||||
err = OutputJSON(r, w, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Error outputting json: %s", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("%d - %s - %+v", w.Code, w.Body.String(), w.HeaderMap)
|
||||
|
||||
if w.Code != 500 {
|
||||
t.Fatalf("Invalid response code for error output: %d", w.Code)
|
||||
}
|
||||
|
||||
if w.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("Result from json must be application/json")
|
||||
}
|
||||
|
||||
// parse output json
|
||||
output := make(map[string]interface{})
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &output); err != nil {
|
||||
t.Fatalf("Could not decode output json: %s", err)
|
||||
}
|
||||
|
||||
if d, ok := output["error"]; !ok || d != E_INVALID_REQUEST {
|
||||
t.Fatalf("Invalid or not found output data: error=%s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedirectResponseJSON(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r := NewResponse(NewTestingStorage())
|
||||
r.SetRedirect("http://localhost:14000")
|
||||
|
||||
err = OutputJSON(r, w, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Error outputting json: %s", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("%d - %s - %+v", w.Code, w.Body.String(), w.HeaderMap)
|
||||
|
||||
if w.Code != 302 {
|
||||
t.Fatalf("Invalid response code for redirect output: %d", w.Code)
|
||||
}
|
||||
|
||||
if w.HeaderMap.Get("Location") != "http://localhost:14000" {
|
||||
t.Fatalf("Invalid response location url: %s", w.HeaderMap.Get("Location"))
|
||||
}
|
||||
}
|
74
vendor/github.com/RangelReale/osin/response_test.go
generated
vendored
Normal file
74
vendor/github.com/RangelReale/osin/response_test.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetRedirectUrl(t *testing.T) {
|
||||
// Make sure we can round-trip state parameters containing special URL characters, both as a query param and in an encoded fragment
|
||||
state := `{"then": "/index.html?a=1&b=%2B#fragment", "nonce": "014f:bff9a07c"}`
|
||||
|
||||
testcases := map[string]struct {
|
||||
URL string
|
||||
Output ResponseData
|
||||
RedirectInFragment bool
|
||||
|
||||
ExpectedURL string
|
||||
}{
|
||||
"query": {
|
||||
URL: "https://foo.com/path?abc=123",
|
||||
Output: ResponseData{"access_token": "12345", "state": state},
|
||||
ExpectedURL: "https://foo.com/path?abc=123&access_token=12345&state=%7B%22then%22%3A+%22%2Findex.html%3Fa%3D1%26b%3D%252B%23fragment%22%2C+%22nonce%22%3A+%22014f%3Abff9a07c%22%7D",
|
||||
},
|
||||
|
||||
// https://tools.ietf.org/html/rfc6749#section-4.2.2
|
||||
// Fragment should be encoded as application/x-www-form-urlencoded (%-escaped, spaces are represented as '+')
|
||||
"fragment": {
|
||||
URL: "https://foo.com/path?abc=123",
|
||||
Output: ResponseData{"access_token": "12345", "state": state},
|
||||
RedirectInFragment: true,
|
||||
ExpectedURL: "https://foo.com/path?abc=123#access_token=12345&state=%7B%22then%22%3A+%22%2Findex.html%3Fa%3D1%26b%3D%252B%23fragment%22%2C+%22nonce%22%3A+%22014f%3Abff9a07c%22%7D",
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range testcases {
|
||||
resp := &Response{
|
||||
Type: REDIRECT,
|
||||
URL: tc.URL,
|
||||
Output: tc.Output,
|
||||
RedirectInFragment: tc.RedirectInFragment,
|
||||
}
|
||||
result, err := resp.GetRedirectUrl()
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
if result != tc.ExpectedURL {
|
||||
t.Errorf("%s: expected\n\t%v, got\n\t%v", k, tc.ExpectedURL, result)
|
||||
continue
|
||||
}
|
||||
|
||||
var params url.Values
|
||||
if tc.RedirectInFragment {
|
||||
params, err = url.ParseQuery(strings.SplitN(result, "#", 2)[1])
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
parsedResult, err := url.Parse(result)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
params = parsedResult.Query()
|
||||
}
|
||||
|
||||
if params["state"][0] != state {
|
||||
t.Errorf("%s: expected\n\t%v, got\n\t%v", k, state, params["state"][0])
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
32
vendor/github.com/RangelReale/osin/server.go
generated
vendored
Normal file
32
vendor/github.com/RangelReale/osin/server.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Server is an OAuth2 implementation
|
||||
type Server struct {
|
||||
Config *ServerConfig
|
||||
Storage Storage
|
||||
AuthorizeTokenGen AuthorizeTokenGen
|
||||
AccessTokenGen AccessTokenGen
|
||||
Now func() time.Time
|
||||
}
|
||||
|
||||
// NewServer creates a new server instance
|
||||
func NewServer(config *ServerConfig, storage Storage) *Server {
|
||||
return &Server{
|
||||
Config: config,
|
||||
Storage: storage,
|
||||
AuthorizeTokenGen: &AuthorizeTokenGenDefault{},
|
||||
AccessTokenGen: &AccessTokenGenDefault{},
|
||||
Now: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
// NewResponse creates a new response for the server
|
||||
func (s *Server) NewResponse() *Response {
|
||||
r := NewResponse(s.Storage)
|
||||
r.ErrorStatusCode = s.Config.ErrorStatusCode
|
||||
return r
|
||||
}
|
59
vendor/github.com/RangelReale/osin/storage.go
generated
vendored
Normal file
59
vendor/github.com/RangelReale/osin/storage.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotFound is the error returned by Storage Get<...> and Load<...> functions in case
|
||||
// no entity is found in the storage. E.g. Storage.GetClient() returns ErrNotFound when
|
||||
// client is not found. All other returned errors must be treated as storage-specific errors,
|
||||
// like "connection lost", "connection refused", etc.
|
||||
ErrNotFound = errors.New("Entity not found")
|
||||
)
|
||||
|
||||
// Storage interface
|
||||
type Storage interface {
|
||||
// Clone the storage if needed. For example, using mgo, you can clone the session with session.Clone
|
||||
// to avoid concurrent access problems.
|
||||
// This is to avoid cloning the connection at each method access.
|
||||
// Can return itself if not a problem.
|
||||
Clone() Storage
|
||||
|
||||
// Close the resources the Storage potentially holds (using Clone for example)
|
||||
Close()
|
||||
|
||||
// GetClient loads the client by id (client_id)
|
||||
GetClient(id string) (Client, error)
|
||||
|
||||
// SaveAuthorize saves authorize data.
|
||||
SaveAuthorize(*AuthorizeData) error
|
||||
|
||||
// LoadAuthorize looks up AuthorizeData by a code.
|
||||
// Client information MUST be loaded together.
|
||||
// Optionally can return error if expired.
|
||||
LoadAuthorize(code string) (*AuthorizeData, error)
|
||||
|
||||
// RemoveAuthorize revokes or deletes the authorization code.
|
||||
RemoveAuthorize(code string) error
|
||||
|
||||
// SaveAccess writes AccessData.
|
||||
// If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
|
||||
SaveAccess(*AccessData) error
|
||||
|
||||
// LoadAccess retrieves access data by token. Client information MUST be loaded together.
|
||||
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
|
||||
// Optionally can return error if expired.
|
||||
LoadAccess(token string) (*AccessData, error)
|
||||
|
||||
// RemoveAccess revokes or deletes an AccessData.
|
||||
RemoveAccess(token string) error
|
||||
|
||||
// LoadRefresh retrieves refresh AccessData. Client information MUST be loaded together.
|
||||
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
|
||||
// Optionally can return error if expired.
|
||||
LoadRefresh(token string) (*AccessData, error)
|
||||
|
||||
// RemoveRefresh revokes or deletes refresh AccessData.
|
||||
RemoveRefresh(token string) error
|
||||
}
|
158
vendor/github.com/RangelReale/osin/storage_test.go
generated
vendored
Normal file
158
vendor/github.com/RangelReale/osin/storage_test.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TestingStorage struct {
|
||||
clients map[string]Client
|
||||
authorize map[string]*AuthorizeData
|
||||
access map[string]*AccessData
|
||||
refresh map[string]string
|
||||
}
|
||||
|
||||
func NewTestingStorage() *TestingStorage {
|
||||
r := &TestingStorage{
|
||||
clients: make(map[string]Client),
|
||||
authorize: make(map[string]*AuthorizeData),
|
||||
access: make(map[string]*AccessData),
|
||||
refresh: make(map[string]string),
|
||||
}
|
||||
|
||||
r.clients["1234"] = &DefaultClient{
|
||||
Id: "1234",
|
||||
Secret: "aabbccdd",
|
||||
RedirectUri: "http://localhost:14000/appauth",
|
||||
}
|
||||
|
||||
r.clients["public-client"] = &DefaultClient{
|
||||
Id: "public-client",
|
||||
RedirectUri: "http://localhost:14000/appauth",
|
||||
}
|
||||
|
||||
r.authorize["9999"] = &AuthorizeData{
|
||||
Client: r.clients["1234"],
|
||||
Code: "9999",
|
||||
ExpiresIn: 3600,
|
||||
CreatedAt: time.Now(),
|
||||
RedirectUri: "http://localhost:14000/appauth",
|
||||
}
|
||||
|
||||
r.access["9999"] = &AccessData{
|
||||
Client: r.clients["1234"],
|
||||
AuthorizeData: r.authorize["9999"],
|
||||
AccessToken: "9999",
|
||||
ExpiresIn: 3600,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
r.access["r9999"] = &AccessData{
|
||||
Client: r.clients["1234"],
|
||||
AuthorizeData: r.authorize["9999"],
|
||||
AccessData: r.access["9999"],
|
||||
AccessToken: "9999",
|
||||
RefreshToken: "r9999",
|
||||
ExpiresIn: 3600,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
r.refresh["r9999"] = "9999"
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *TestingStorage) Clone() Storage {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *TestingStorage) Close() {
|
||||
}
|
||||
|
||||
func (s *TestingStorage) GetClient(id string) (Client, error) {
|
||||
if c, ok := s.clients[id]; ok {
|
||||
return c, nil
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestingStorage) SetClient(id string, client Client) error {
|
||||
s.clients[id] = client
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestingStorage) SaveAuthorize(data *AuthorizeData) error {
|
||||
s.authorize[data.Code] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestingStorage) LoadAuthorize(code string) (*AuthorizeData, error) {
|
||||
if d, ok := s.authorize[code]; ok {
|
||||
return d, nil
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestingStorage) RemoveAuthorize(code string) error {
|
||||
delete(s.authorize, code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestingStorage) SaveAccess(data *AccessData) error {
|
||||
s.access[data.AccessToken] = data
|
||||
if data.RefreshToken != "" {
|
||||
s.refresh[data.RefreshToken] = data.AccessToken
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestingStorage) LoadAccess(code string) (*AccessData, error) {
|
||||
if d, ok := s.access[code]; ok {
|
||||
return d, nil
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestingStorage) RemoveAccess(code string) error {
|
||||
delete(s.access, code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TestingStorage) LoadRefresh(code string) (*AccessData, error) {
|
||||
if d, ok := s.refresh[code]; ok {
|
||||
return s.LoadAccess(d)
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (s *TestingStorage) RemoveRefresh(code string) error {
|
||||
delete(s.refresh, code)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Predictable testing token generation
|
||||
|
||||
type TestingAuthorizeTokenGen struct {
|
||||
counter int64
|
||||
}
|
||||
|
||||
func (a *TestingAuthorizeTokenGen) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) {
|
||||
a.counter++
|
||||
return strconv.FormatInt(a.counter, 10), nil
|
||||
}
|
||||
|
||||
type TestingAccessTokenGen struct {
|
||||
acounter int64
|
||||
rcounter int64
|
||||
}
|
||||
|
||||
func (a *TestingAccessTokenGen) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
|
||||
a.acounter++
|
||||
accesstoken = strconv.FormatInt(a.acounter, 10)
|
||||
|
||||
if generaterefresh {
|
||||
a.rcounter++
|
||||
refreshtoken = "r" + strconv.FormatInt(a.rcounter, 10)
|
||||
}
|
||||
return
|
||||
}
|
33
vendor/github.com/RangelReale/osin/tokengen.go
generated
vendored
Normal file
33
vendor/github.com/RangelReale/osin/tokengen.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
)
|
||||
|
||||
// AuthorizeTokenGenDefault is the default authorization token generator
|
||||
type AuthorizeTokenGenDefault struct {
|
||||
}
|
||||
|
||||
// GenerateAuthorizeToken generates a base64-encoded UUID code
|
||||
func (a *AuthorizeTokenGenDefault) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) {
|
||||
token := uuid.NewRandom()
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(token)), nil
|
||||
}
|
||||
|
||||
// AccessTokenGenDefault is the default authorization token generator
|
||||
type AccessTokenGenDefault struct {
|
||||
}
|
||||
|
||||
// GenerateAccessToken generates base64-encoded UUID access and refresh tokens
|
||||
func (a *AccessTokenGenDefault) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
|
||||
token := uuid.NewRandom()
|
||||
accesstoken = base64.RawURLEncoding.EncodeToString([]byte(token))
|
||||
|
||||
if generaterefresh {
|
||||
rtoken := uuid.NewRandom()
|
||||
refreshtoken = base64.RawURLEncoding.EncodeToString([]byte(rtoken))
|
||||
}
|
||||
return
|
||||
}
|
114
vendor/github.com/RangelReale/osin/urivalidate.go
generated
vendored
Normal file
114
vendor/github.com/RangelReale/osin/urivalidate.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// error returned when validation don't match
|
||||
type UriValidationError string
|
||||
|
||||
func (e UriValidationError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func newUriValidationError(msg string, base string, redirect string) UriValidationError {
|
||||
return UriValidationError(fmt.Sprintf("%s: %s / %s", msg, base, redirect))
|
||||
}
|
||||
|
||||
// ValidateUriList validates that redirectUri is contained in baseUriList.
|
||||
// baseUriList may be a string separated by separator.
|
||||
// If separator is blank, validate only 1 URI.
|
||||
func ValidateUriList(baseUriList string, redirectUri string, separator string) error {
|
||||
// make a list of uris
|
||||
var slist []string
|
||||
if separator != "" {
|
||||
slist = strings.Split(baseUriList, separator)
|
||||
} else {
|
||||
slist = make([]string, 0)
|
||||
slist = append(slist, baseUriList)
|
||||
}
|
||||
|
||||
for _, sitem := range slist {
|
||||
err := ValidateUri(sitem, redirectUri)
|
||||
// validated, return no error
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if there was an error that is not a validation error, return it
|
||||
if _, iok := err.(UriValidationError); !iok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return newUriValidationError("urls don't validate", baseUriList, redirectUri)
|
||||
}
|
||||
|
||||
// ValidateUri validates that redirectUri is contained in baseUri
|
||||
func ValidateUri(baseUri string, redirectUri string) error {
|
||||
if baseUri == "" || redirectUri == "" {
|
||||
return errors.New("urls cannot be blank.")
|
||||
}
|
||||
|
||||
// parse base url
|
||||
base, err := url.Parse(baseUri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse passed url
|
||||
redirect, err := url.Parse(redirectUri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// must not have fragment
|
||||
if base.Fragment != "" || redirect.Fragment != "" {
|
||||
return errors.New("url must not include fragment.")
|
||||
}
|
||||
|
||||
// check if urls match
|
||||
if base.Scheme != redirect.Scheme {
|
||||
return newUriValidationError("scheme mismatch", baseUri, redirectUri)
|
||||
}
|
||||
if base.Host != redirect.Host {
|
||||
return newUriValidationError("host mismatch", baseUri, redirectUri)
|
||||
}
|
||||
|
||||
// allow exact path matches
|
||||
if base.Path == redirect.Path {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensure prefix matches are actually subpaths
|
||||
requiredPrefix := strings.TrimRight(base.Path, "/") + "/"
|
||||
if !strings.HasPrefix(redirect.Path, requiredPrefix) {
|
||||
return newUriValidationError("path is not a subpath", baseUri, redirectUri)
|
||||
}
|
||||
|
||||
// ensure prefix matches don't contain path traversals
|
||||
for _, s := range strings.Split(strings.TrimPrefix(redirect.Path, requiredPrefix), "/") {
|
||||
if s == ".." {
|
||||
return newUriValidationError("subpath cannot contain path traversal", baseUri, redirectUri)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the first uri from an uri list
|
||||
func FirstUri(baseUriList string, separator string) string {
|
||||
if separator != "" {
|
||||
slist := strings.Split(baseUriList, separator)
|
||||
if len(slist) > 0 {
|
||||
return slist[0]
|
||||
}
|
||||
} else {
|
||||
return baseUriList
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
120
vendor/github.com/RangelReale/osin/urivalidate_test.go
generated
vendored
Normal file
120
vendor/github.com/RangelReale/osin/urivalidate_test.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestURIValidate(t *testing.T) {
|
||||
valid := [][]string{
|
||||
{
|
||||
// Exact match
|
||||
"http://localhost:14000/appauth",
|
||||
"http://localhost:14000/appauth",
|
||||
},
|
||||
{
|
||||
// Trailing slash
|
||||
"http://www.google.com/myapp",
|
||||
"http://www.google.com/myapp/",
|
||||
},
|
||||
{
|
||||
// Exact match with trailing slash
|
||||
"http://www.google.com/myapp/",
|
||||
"http://www.google.com/myapp/",
|
||||
},
|
||||
{
|
||||
// Subpath
|
||||
"http://www.google.com/myapp",
|
||||
"http://www.google.com/myapp/interface/implementation",
|
||||
},
|
||||
{
|
||||
// Subpath with trailing slash
|
||||
"http://www.google.com/myapp/",
|
||||
"http://www.google.com/myapp/interface/implementation",
|
||||
},
|
||||
{
|
||||
// Subpath with things that are close to path traversals, but aren't
|
||||
"http://www.google.com/myapp",
|
||||
"http://www.google.com/myapp/.../..implementation../...",
|
||||
},
|
||||
{
|
||||
// If the allowed basepath contains path traversals, allow them?
|
||||
"http://www.google.com/traversal/../allowed",
|
||||
"http://www.google.com/traversal/../allowed/with/subpath",
|
||||
},
|
||||
}
|
||||
for _, v := range valid {
|
||||
if err := ValidateUri(v[0], v[1]); err != nil {
|
||||
t.Errorf("Expected ValidateUri(%s, %s) to succeed, got %v", v[0], v[1], err)
|
||||
}
|
||||
}
|
||||
|
||||
invalid := [][]string{
|
||||
{
|
||||
// Doesn't satisfy base path
|
||||
"http://localhost:14000/appauth",
|
||||
"http://localhost:14000/app",
|
||||
},
|
||||
{
|
||||
// Doesn't satisfy base path
|
||||
"http://localhost:14000/app/",
|
||||
"http://localhost:14000/app",
|
||||
},
|
||||
{
|
||||
// Not a subpath of base path
|
||||
"http://localhost:14000/appauth",
|
||||
"http://localhost:14000/appauthmodifiedpath",
|
||||
},
|
||||
{
|
||||
// Host mismatch
|
||||
"http://www.google.com/myapp",
|
||||
"http://www2.google.com/myapp",
|
||||
},
|
||||
{
|
||||
// Scheme mismatch
|
||||
"http://www.google.com/myapp",
|
||||
"https://www.google.com/myapp",
|
||||
},
|
||||
{
|
||||
// Path traversal
|
||||
"http://www.google.com/myapp",
|
||||
"http://www.google.com/myapp/..",
|
||||
},
|
||||
{
|
||||
// Embedded path traversal
|
||||
"http://www.google.com/myapp",
|
||||
"http://www.google.com/myapp/../test",
|
||||
},
|
||||
{
|
||||
// Not a subpath
|
||||
"http://www.google.com/myapp",
|
||||
"http://www.google.com/myapp../test",
|
||||
},
|
||||
}
|
||||
for _, v := range invalid {
|
||||
if err := ValidateUri(v[0], v[1]); err == nil {
|
||||
t.Errorf("Expected ValidateUri(%s, %s) to fail", v[0], v[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestURIListValidate(t *testing.T) {
|
||||
// V1
|
||||
if err := ValidateUriList("http://localhost:14000/appauth", "http://localhost:14000/appauth", ""); err != nil {
|
||||
t.Errorf("V1: %s", err)
|
||||
}
|
||||
|
||||
// V2
|
||||
if err := ValidateUriList("http://localhost:14000/appauth", "http://localhost:14000/app", ""); err == nil {
|
||||
t.Error("V2 should have failed")
|
||||
}
|
||||
|
||||
// V3
|
||||
if err := ValidateUriList("http://xxx:14000/appauth;http://localhost:14000/appauth", "http://localhost:14000/appauth", ";"); err != nil {
|
||||
t.Errorf("V3: %s", err)
|
||||
}
|
||||
|
||||
// V4
|
||||
if err := ValidateUriList("http://xxx:14000/appauth;http://localhost:14000/appauth", "http://localhost:14000/app", ";"); err == nil {
|
||||
t.Error("V4 should have failed")
|
||||
}
|
||||
}
|
108
vendor/github.com/RangelReale/osin/util.go
generated
vendored
Normal file
108
vendor/github.com/RangelReale/osin/util.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse basic authentication header
|
||||
type BasicAuth struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Parse bearer authentication header
|
||||
type BearerAuth struct {
|
||||
Code string
|
||||
}
|
||||
|
||||
// CheckClientSecret determines whether the given secret matches a secret held by the client.
|
||||
// Public clients return true for a secret of ""
|
||||
func CheckClientSecret(client Client, secret string) bool {
|
||||
switch client := client.(type) {
|
||||
case ClientSecretMatcher:
|
||||
// Prefer the more secure method of giving the secret to the client for comparison
|
||||
return client.ClientSecretMatches(secret)
|
||||
default:
|
||||
// Fallback to the less secure method of extracting the plain text secret from the client for comparison
|
||||
return client.GetSecret() == secret
|
||||
}
|
||||
}
|
||||
|
||||
// Return authorization header data
|
||||
func CheckBasicAuth(r *http.Request) (*BasicAuth, error) {
|
||||
if r.Header.Get("Authorization") == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||
if len(s) != 2 || s[0] != "Basic" {
|
||||
return nil, errors.New("Invalid authorization header")
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pair := strings.SplitN(string(b), ":", 2)
|
||||
if len(pair) != 2 {
|
||||
return nil, errors.New("Invalid authorization message")
|
||||
}
|
||||
|
||||
return &BasicAuth{Username: pair[0], Password: pair[1]}, nil
|
||||
}
|
||||
|
||||
// Return "Bearer" token from request. The header has precedence over query string.
|
||||
func CheckBearerAuth(r *http.Request) *BearerAuth {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
authForm := r.Form.Get("code")
|
||||
if authHeader == "" && authForm == "" {
|
||||
return nil
|
||||
}
|
||||
token := authForm
|
||||
if authHeader != "" {
|
||||
s := strings.SplitN(authHeader, " ", 2)
|
||||
if (len(s) != 2 || strings.ToLower(s[0]) != "bearer") && token == "" {
|
||||
return nil
|
||||
}
|
||||
//Use authorization header token only if token type is bearer else query string access token would be returned
|
||||
if len(s) > 0 && strings.ToLower(s[0]) == "bearer" {
|
||||
token = s[1]
|
||||
}
|
||||
}
|
||||
return &BearerAuth{Code: token}
|
||||
}
|
||||
|
||||
// getClientAuth checks client basic authentication in params if allowed,
|
||||
// otherwise gets it from the header.
|
||||
// Sets an error on the response if no auth is present or a server error occurs.
|
||||
func getClientAuth(w *Response, r *http.Request, allowQueryParams bool) *BasicAuth {
|
||||
|
||||
if allowQueryParams {
|
||||
// Allow for auth without password
|
||||
if _, hasSecret := r.Form["client_secret"]; hasSecret {
|
||||
auth := &BasicAuth{
|
||||
Username: r.Form.Get("client_id"),
|
||||
Password: r.Form.Get("client_secret"),
|
||||
}
|
||||
if auth.Username != "" {
|
||||
return auth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auth, err := CheckBasicAuth(r)
|
||||
if err != nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = err
|
||||
return nil
|
||||
}
|
||||
if auth == nil {
|
||||
w.SetError(E_INVALID_REQUEST, "")
|
||||
w.InternalError = errors.New("Client authentication not sent")
|
||||
return nil
|
||||
}
|
||||
return auth
|
||||
}
|
136
vendor/github.com/RangelReale/osin/util_test.go
generated
vendored
Normal file
136
vendor/github.com/RangelReale/osin/util_test.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
package osin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
badAuthValue = "Digest XHHHHHHH"
|
||||
goodAuthValue = "Basic dGVzdDp0ZXN0"
|
||||
goodBearerAuthValue = "Bearer BGFVTDUJDp0ZXN0"
|
||||
)
|
||||
|
||||
func TestBasicAuth(t *testing.T) {
|
||||
r := &http.Request{Header: make(http.Header)}
|
||||
|
||||
// Without any header
|
||||
if b, err := CheckBasicAuth(r); b != nil || err != nil {
|
||||
t.Errorf("Validated basic auth without header")
|
||||
}
|
||||
|
||||
// with invalid header
|
||||
r.Header.Set("Authorization", badAuthValue)
|
||||
b, err := CheckBasicAuth(r)
|
||||
if b != nil || err == nil {
|
||||
t.Errorf("Validated invalid auth")
|
||||
return
|
||||
}
|
||||
|
||||
// with valid header
|
||||
r.Header.Set("Authorization", goodAuthValue)
|
||||
b, err = CheckBasicAuth(r)
|
||||
if b == nil || err != nil {
|
||||
t.Errorf("Could not extract basic auth")
|
||||
return
|
||||
}
|
||||
|
||||
// check extracted auth data
|
||||
if b.Username != "test" || b.Password != "test" {
|
||||
t.Errorf("Error decoding basic auth")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClientAuth(t *testing.T) {
|
||||
|
||||
urlWithSecret, _ := url.Parse("http://host.tld/path?client_id=xxx&client_secret=yyy")
|
||||
urlWithEmptySecret, _ := url.Parse("http://host.tld/path?client_id=xxx&client_secret=")
|
||||
urlNoSecret, _ := url.Parse("http://host.tld/path?client_id=xxx")
|
||||
|
||||
headerNoAuth := make(http.Header)
|
||||
headerBadAuth := make(http.Header)
|
||||
headerBadAuth.Set("Authorization", badAuthValue)
|
||||
headerOKAuth := make(http.Header)
|
||||
headerOKAuth.Set("Authorization", goodAuthValue)
|
||||
|
||||
var tests = []struct {
|
||||
header http.Header
|
||||
url *url.URL
|
||||
allowQueryParams bool
|
||||
expectAuth bool
|
||||
}{
|
||||
{headerNoAuth, urlWithSecret, true, true},
|
||||
{headerNoAuth, urlWithSecret, false, false},
|
||||
{headerNoAuth, urlWithEmptySecret, true, true},
|
||||
{headerNoAuth, urlWithEmptySecret, false, false},
|
||||
{headerNoAuth, urlNoSecret, true, false},
|
||||
{headerNoAuth, urlNoSecret, false, false},
|
||||
|
||||
{headerBadAuth, urlWithSecret, true, true},
|
||||
{headerBadAuth, urlWithSecret, false, false},
|
||||
{headerBadAuth, urlWithEmptySecret, true, true},
|
||||
{headerBadAuth, urlWithEmptySecret, false, false},
|
||||
{headerBadAuth, urlNoSecret, true, false},
|
||||
{headerBadAuth, urlNoSecret, false, false},
|
||||
|
||||
{headerOKAuth, urlWithSecret, true, true},
|
||||
{headerOKAuth, urlWithSecret, false, true},
|
||||
{headerOKAuth, urlWithEmptySecret, true, true},
|
||||
{headerOKAuth, urlWithEmptySecret, false, true},
|
||||
{headerOKAuth, urlNoSecret, true, true},
|
||||
{headerOKAuth, urlNoSecret, false, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
w := new(Response)
|
||||
r := &http.Request{Header: tt.header, URL: tt.url}
|
||||
r.ParseForm()
|
||||
auth := getClientAuth(w, r, tt.allowQueryParams)
|
||||
if tt.expectAuth && auth == nil {
|
||||
t.Errorf("Auth should not be nil for %v", tt)
|
||||
} else if !tt.expectAuth && auth != nil {
|
||||
t.Errorf("Auth should be nil for %v", tt)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBearerAuth(t *testing.T) {
|
||||
r := &http.Request{Header: make(http.Header)}
|
||||
|
||||
// Without any header
|
||||
if b := CheckBearerAuth(r); b != nil {
|
||||
t.Errorf("Validated bearer auth without header")
|
||||
}
|
||||
|
||||
// with invalid header
|
||||
r.Header.Set("Authorization", badAuthValue)
|
||||
b := CheckBearerAuth(r)
|
||||
if b != nil {
|
||||
t.Errorf("Validated invalid auth")
|
||||
return
|
||||
}
|
||||
|
||||
// with valid header
|
||||
r.Header.Set("Authorization", goodBearerAuthValue)
|
||||
b = CheckBearerAuth(r)
|
||||
if b == nil {
|
||||
t.Errorf("Could not extract bearer auth")
|
||||
return
|
||||
}
|
||||
|
||||
// check extracted auth data
|
||||
if b.Code != "BGFVTDUJDp0ZXN0" {
|
||||
t.Errorf("Error decoding bearer auth")
|
||||
}
|
||||
|
||||
// extracts bearer auth from query string
|
||||
url, _ := url.Parse("http://host.tld/path?code=XYZ")
|
||||
r = &http.Request{URL: url}
|
||||
r.ParseForm()
|
||||
b = CheckBearerAuth(r)
|
||||
if b.Code != "XYZ" {
|
||||
t.Errorf("Error decoding bearer auth")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user