-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
792 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/.idea | ||
test-results/ | ||
tmp/ | ||
routes/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Revel oauth2 protected api with Golang oauth2 server | ||
|
||
## Getting Started | ||
|
||
Dependencies | ||
|
||
<pre> | ||
go get github.com/revel/revel | ||
go get https://github.com/RangelReale/osin | ||
go get golang.org/x/oauth2 | ||
</pre> | ||
|
||
#### Start api with server: | ||
|
||
revel run restful | ||
|
||
#### How to get token? | ||
|
||
Open url <pre style="display: inline !important;">http://localhost:14000/?response_type=code&client_id=1234&redirect_uri=http://localhost:14000/appauth</pre> | ||
|
||
You will get json: | ||
<pre> | ||
{ | ||
"token": "bitv8UqUSiSOQ6bWe3TKjA" | ||
} | ||
</pre> | ||
|
||
#### How to get check if API is protected with token? | ||
Open url <pre style="display: inline !important;">http://localhost:14000/example?access_token=somegarbagehere</pre> and you will see json | ||
<pre> | ||
{ | ||
"error": 1 | ||
} | ||
</pre> | ||
|
||
|
||
#### Project is using mysql storage for client id and client secret. | ||
|
||
<pre> | ||
func NewStorage() *Storage { | ||
|
||
r := &Storage{ | ||
clients: make(map[string]osin.Client), | ||
authorize: make(map[string]*osin.AuthorizeData), | ||
access: make(map[string]*osin.AccessData), | ||
refresh: make(map[string]string), | ||
} | ||
|
||
db, _ := sql.Open("mysql", "oauth2:password@/oauth2?charset=utf8") | ||
rows, _ := db.Query("SELECT * FROM Oauth2Client") | ||
|
||
for rows.Next() { | ||
|
||
var Id int | ||
var Client string | ||
var Secret string | ||
var RedirectUrl string | ||
|
||
rows.Scan(&Id, &Client, &Secret, &RedirectUrl) | ||
|
||
log.Println(Client) | ||
log.Println(Secret) | ||
log.Println(RedirectUrl) | ||
r.clients[Client] = &osin.DefaultClient{ | ||
Id: Client, | ||
Secret: Secret, | ||
RedirectUri: RedirectUrl, | ||
} | ||
|
||
log.Println(r.clients) | ||
} | ||
|
||
return r | ||
} | ||
</pre> | ||
|
||
#### Do not forget to import db file located at sql/oauth2.sql | ||
|
||
<pre> | ||
|
||
CREATE TABLE `Oauth2Client` ( | ||
`Id` int(11) NOT NULL AUTO_INCREMENT, | ||
`Client` varchar(255) NOT NULL, | ||
`Secret` varchar(255) NOT NULL, | ||
`RedirectUrl` varchar(255) NOT NULL, | ||
PRIMARY KEY (`Id`) | ||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; | ||
|
||
</pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package controllers | ||
|
||
import ( | ||
"github.com/revel/revel" | ||
"github.com/RangelReale/osin" | ||
"fmt" | ||
"golang.org/x/oauth2" | ||
"restful/app/helpers" | ||
) | ||
var cfg = osin.NewServerConfig() | ||
var server = osin.NewServer(cfg, helpers.NewStorage()) | ||
|
||
type App struct { | ||
*revel.Controller | ||
} | ||
|
||
func (c App) Init() revel.Result{ | ||
|
||
if c.Controller.Action != "App.Index" && | ||
c.Controller.Action != "App.Token" && | ||
c.Controller.Action != "App.GetToken" { | ||
|
||
if c.Session["access_token"] != c.Params.Get("access_token"){ | ||
mp := map[string]interface{}{ | ||
"error":1, | ||
} | ||
return c.RenderJson(mp) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
func (c App) Index() revel.Result { | ||
|
||
r := c.Request.Request | ||
w := c.Response.Out | ||
|
||
resp := server.NewResponse() | ||
defer resp.Close() | ||
|
||
ar := server.HandleAuthorizeRequest(resp, r) | ||
ar.Authorized = true | ||
server.FinishAuthorizeRequest(resp, r, ar) | ||
osin.OutputJSON(resp, w, r) | ||
|
||
return nil | ||
} | ||
|
||
func (c App) Token() revel.Result { | ||
|
||
r := c.Request.Request | ||
w := c.Response.Out | ||
|
||
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) | ||
|
||
return nil | ||
} | ||
|
||
func (c App) GetToken() revel.Result{ | ||
|
||
code := c.Params.Get("code") | ||
|
||
githubConfig := &oauth2.Config{ | ||
|
||
ClientID: "nikola", // change this to yours | ||
ClientSecret: "nikola", | ||
RedirectURL: "http://localhost:14000/appauth", // change this to your webserver adddress | ||
Scopes: []string{"user:email"}, | ||
Endpoint: oauth2.Endpoint{ | ||
AuthURL: "http://localhost:14000/appauth", | ||
TokenURL: "http://localhost:14000/token", | ||
}, | ||
} | ||
|
||
tok, _ := githubConfig.Exchange(oauth2.NoContext, code) | ||
|
||
mp := map[string]interface{}{ | ||
"token":tok.AccessToken, | ||
} | ||
|
||
c.Session["access_token"] = tok.AccessToken | ||
|
||
return c.RenderJson(mp) | ||
} | ||
|
||
func (c App) Example() revel.Result { | ||
mp := map[string]interface{}{ | ||
"great":1, | ||
} | ||
|
||
return c.RenderJson(mp) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package controllers | ||
import ( | ||
"github.com/revel/revel" | ||
) | ||
func init() { | ||
revel.InterceptMethod(App.Init, revel.BEFORE) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package helpers | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"github.com/RangelReale/osin" | ||
_ "github.com/go-sql-driver/mysql" | ||
"database/sql" | ||
"log" | ||
) | ||
|
||
type Storage struct { | ||
clients map[string]osin.Client | ||
authorize map[string]*osin.AuthorizeData | ||
access map[string]*osin.AccessData | ||
refresh map[string]string | ||
} | ||
|
||
func NewStorage() *Storage { | ||
|
||
r := &Storage{ | ||
clients: make(map[string]osin.Client), | ||
authorize: make(map[string]*osin.AuthorizeData), | ||
access: make(map[string]*osin.AccessData), | ||
refresh: make(map[string]string), | ||
} | ||
|
||
db, _ := sql.Open("mysql", "oauth2:password@/oauth2?charset=utf8") | ||
rows, _ := db.Query("SELECT * FROM Oauth2Client") | ||
|
||
for rows.Next() { | ||
|
||
var Id int | ||
var Client string | ||
var Secret string | ||
var RedirectUrl string | ||
|
||
rows.Scan(&Id, &Client, &Secret, &RedirectUrl) | ||
|
||
log.Println(Client) | ||
log.Println(Secret) | ||
log.Println(RedirectUrl) | ||
r.clients[Client] = &osin.DefaultClient{ | ||
Id: Client, | ||
Secret: Secret, | ||
RedirectUri: RedirectUrl, | ||
} | ||
|
||
log.Println(r.clients) | ||
} | ||
|
||
return r | ||
} | ||
|
||
func (s *Storage) Clone() osin.Storage { | ||
return s | ||
} | ||
|
||
func (s *Storage) Close() { | ||
} | ||
|
||
func (s *Storage) GetClient(id string) (osin.Client, error) { | ||
fmt.Printf("GetClient: %s\n", id) | ||
if c, ok := s.clients[id]; ok { | ||
return c, nil | ||
} | ||
return nil, errors.New("Client not found") | ||
} | ||
|
||
func (s *Storage) SetClient(id string, client osin.Client) error { | ||
fmt.Printf("SetClient: %s\n", id) | ||
s.clients[id] = client | ||
return nil | ||
} | ||
|
||
func (s *Storage) SaveAuthorize(data *osin.AuthorizeData) error { | ||
fmt.Printf("SaveAuthorize: %s\n", data.Code) | ||
s.authorize[data.Code] = data | ||
return nil | ||
} | ||
|
||
func (s *Storage) LoadAuthorize(code string) (*osin.AuthorizeData, error) { | ||
fmt.Printf("LoadAuthorize: %s\n", code) | ||
if d, ok := s.authorize[code]; ok { | ||
return d, nil | ||
} | ||
return nil, errors.New("Authorize not found") | ||
} | ||
|
||
func (s *Storage) RemoveAuthorize(code string) error { | ||
fmt.Printf("RemoveAuthorize: %s\n", code) | ||
delete(s.authorize, code) | ||
return nil | ||
} | ||
|
||
func (s *Storage) 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 *Storage) LoadAccess(code string) (*osin.AccessData, error) { | ||
fmt.Printf("LoadAccess: %s\n", code) | ||
if d, ok := s.access[code]; ok { | ||
return d, nil | ||
} | ||
return nil, errors.New("Access not found") | ||
} | ||
|
||
func (s *Storage) RemoveAccess(code string) error { | ||
fmt.Printf("RemoveAccess: %s\n", code) | ||
delete(s.access, code) | ||
return nil | ||
} | ||
|
||
func (s *Storage) 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, errors.New("Refresh not found") | ||
} | ||
|
||
func (s *Storage) RemoveRefresh(code string) error { | ||
fmt.Printf("RemoveRefresh: %s\n", code) | ||
delete(s.refresh, code) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package app | ||
|
||
import "github.com/revel/revel" | ||
|
||
func init() { | ||
// Filters is the default set of global filters. | ||
revel.Filters = []revel.Filter{ | ||
revel.PanicFilter, // Recover from panics and display an error page instead. | ||
revel.RouterFilter, // Use the routing table to select the right Action | ||
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters. | ||
revel.ParamsFilter, // Parse parameters into Controller.Params. | ||
revel.SessionFilter, // Restore and write the session cookie. | ||
revel.FlashFilter, // Restore and write the flash cookie. | ||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie. | ||
revel.I18nFilter, // Resolve the requested language | ||
HeaderFilter, // Add some security based headers | ||
revel.InterceptorFilter, // Run interceptors around the action. | ||
revel.CompressFilter, // Compress the result. | ||
revel.ActionInvoker, // Invoke the action. | ||
} | ||
|
||
// register startup functions with OnAppStart | ||
// ( order dependent ) | ||
// revel.OnAppStart(InitDB) | ||
// revel.OnAppStart(FillCache) | ||
} | ||
|
||
// TODO turn this into revel.HeaderFilter | ||
// should probably also have a filter for CSRF | ||
// not sure if it can go in the same filter or not | ||
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) { | ||
// Add some common security headers | ||
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN") | ||
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block") | ||
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff") | ||
|
||
fc[0](c, fc[1:]) // Execute the next filter stage. | ||
} |
Oops, something went wrong.