Skip to content

Commit

Permalink
Merge branch 'release/0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
goors committed Sep 22, 2015
2 parents d7e3710 + ca0f01b commit fa9aa77
Show file tree
Hide file tree
Showing 18 changed files with 792 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.idea
test-results/
tmp/
routes/
89 changes: 89 additions & 0 deletions README.md
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>
102 changes: 102 additions & 0 deletions app/controllers/app.go
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)
}
7 changes: 7 additions & 0 deletions app/controllers/init.go
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)
}
131 changes: 131 additions & 0 deletions app/helpers/storage.go
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
}
38 changes: 38 additions & 0 deletions app/init.go
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.
}
Loading

0 comments on commit fa9aa77

Please sign in to comment.