Skip to content

Commit

Permalink
Auth Provider (micro#1309)
Browse files Browse the repository at this point in the history
* auth provider mock interface

* Auth Provider Options

* Implement API Server Auth Package

* Add weh utils

* Add Login URL

* Auth Provider Options

* Add auth provider scope and setting token in cookie

* Remove auth_login_url flag

Co-authored-by: Asim Aslam <asim@aslam.me>
Co-authored-by: Ben Toogood <ben@micro.mu>
  • Loading branch information
3 people committed Mar 7, 2020
1 parent 8ee5607 commit 9a7a65f
Show file tree
Hide file tree
Showing 10 changed files with 350 additions and 1 deletion.
70 changes: 70 additions & 0 deletions api/server/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package auth

import (
"net/http"
"strings"

"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/metadata"
)

// CombinedAuthHandler wraps a server and authenticates requests
func CombinedAuthHandler(h http.Handler) http.Handler {
return authHandler{
handler: h,
auth: auth.DefaultAuth,
}
}

type authHandler struct {
handler http.Handler
auth auth.Auth
}

const (
// BearerScheme is the prefix in the auth header
BearerScheme = "Bearer "
)

func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
loginURL := h.auth.Options().LoginURL

// Return if the user disabled auth on this endpoint
excludes := h.auth.Options().Exclude
if len(loginURL) > 0 {
excludes = append(excludes, loginURL)
}
for _, e := range excludes {
if e == req.URL.Path {
h.handler.ServeHTTP(w, req)
return
}
}

var token string
if header, ok := metadata.Get(req.Context(), "Authorization"); ok {
// Extract the auth token from the request
if strings.HasPrefix(header, BearerScheme) {
token = header[len(BearerScheme):]
}
} else {
// Get the token out the cookies if not provided in headers
if c, err := req.Cookie(auth.CookieName); err != nil && c != nil {
token = c.Value
}
}

// If the token is valid, allow the request
if _, err := h.auth.Verify(token); err == nil {
h.handler.ServeHTTP(w, req)
return
}

// If there is no auth login url set, 401
if loginURL == "" {
w.WriteHeader(401)
}

// Redirect to the login path
http.Redirect(w, req, loginURL, http.StatusTemporaryRedirect)
}
3 changes: 3 additions & 0 deletions api/server/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"os"
"sync"

"github.com/micro/go-micro/v2/api/server/auth"

"github.com/gorilla/handlers"
"github.com/micro/go-micro/v2/api/server"
"github.com/micro/go-micro/v2/api/server/cors"
Expand Down Expand Up @@ -47,6 +49,7 @@ func (s *httpServer) Init(opts ...server.Option) error {

func (s *httpServer) Handle(path string, handler http.Handler) {
h := handlers.CombinedLoggingHandler(os.Stdout, handler)
h = auth.CombinedAuthHandler(handler)

if s.opts.EnableCORS {
h = cors.CombinedCORSHandler(h)
Expand Down
5 changes: 4 additions & 1 deletion auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Role struct {

// Account provided by an auth provider
type Account struct {
// ID of the account (UUID or email)
// ID of the account (UUIDV4, email or username)
Id string `json:"id"`
// Token used to authenticate
Token string `json:"token"`
Expand All @@ -62,6 +62,9 @@ const (
// MetadataKey is the key used when storing the account
// in metadata
MetadataKey = "auth-account"
// CookieName is the name of the cookie which stores the
// auth token
CookieName = "micro-token"
)

// AccountFromContext gets the account from the context, which
Expand Down
20 changes: 20 additions & 0 deletions auth/options.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package auth

import "github.com/micro/go-micro/v2/auth/provider"

type Options struct {
// Token is an auth token
Token string
Expand All @@ -9,6 +11,10 @@ type Options struct {
PrivateKey string
// Endpoints to exclude
Exclude []string
// Provider is an auth provider
Provider provider.Provider
// LoginURL is the relative url path where a user can login
LoginURL string
}

type Option func(o *Options)
Expand Down Expand Up @@ -41,6 +47,20 @@ func Token(t string) Option {
}
}

// Provider set the auth provider
func Provider(p provider.Provider) Option {
return func(o *Options) {
o.Provider = p
}
}

// LoginURL sets the auth LoginURL
func LoginURL(url string) Option {
return func(o *Options) {
o.LoginURL = url
}
}

type GenerateOptions struct {
// Metadata associated with the account
Metadata map[string]string
Expand Down
34 changes: 34 additions & 0 deletions auth/provider/basic/basic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package basic

import (
"github.com/micro/go-micro/v2/auth/provider"
)

// NewProvider returns an initialised basic provider
func NewProvider(opts ...provider.Option) provider.Provider {
var options provider.Options
for _, o := range opts {
o(&options)
}
return &basic{options}
}

type basic struct {
opts provider.Options
}

func (b *basic) String() string {
return "basic"
}

func (b *basic) Options() provider.Options {
return b.opts
}

func (b *basic) Endpoint() string {
return ""
}

func (b *basic) Redirect() string {
return ""
}
42 changes: 42 additions & 0 deletions auth/provider/oauth/oauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package oauth

import (
"fmt"

"github.com/micro/go-micro/v2/auth/provider"
)

// NewProvider returns an initialised oauth provider
func NewProvider(opts ...provider.Option) provider.Provider {
var options provider.Options
for _, o := range opts {
o(&options)
}
return &oauth{options}
}

type oauth struct {
opts provider.Options
}

func (o *oauth) String() string {
return "oauth"
}

func (o *oauth) Options() provider.Options {
return o.opts
}

func (o *oauth) Endpoint() string {
s := fmt.Sprintf("%v?client_id=%v", o.opts.Endpoint, o.opts.ClientID)

if scope := o.opts.Scope; len(scope) > 0 {
s = fmt.Sprintf("%v&scope=%v", s, scope)
}

return s
}

func (o *oauth) Redirect() string {
return o.opts.Redirect
}
47 changes: 47 additions & 0 deletions auth/provider/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package provider

// Option returns a function which sets an option
type Option func(*Options)

// Options a provider can have
type Options struct {
// ClientID is the application's ID.
ClientID string
// ClientSecret is the application's secret.
ClientSecret string
// Endpoint for the provider
Endpoint string
// Redirect url incase of UI
Redirect string
// Scope of the oauth request
Scope string
}

// Credentials is an option which sets the client id and secret
func Credentials(id, secret string) Option {
return func(o *Options) {
o.ClientID = id
o.ClientSecret = secret
}
}

// Endpoint sets the endpoint option
func Endpoint(e string) Option {
return func(o *Options) {
o.Endpoint = e
}
}

// Redirect sets the Redirect option
func Redirect(r string) Option {
return func(o *Options) {
o.Redirect = r
}
}

// Scope sets the oauth scope
func Scope(s string) Option {
return func(o *Options) {
o.Scope = s
}
}
28 changes: 28 additions & 0 deletions auth/provider/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Package provider is an external auth provider e.g oauth
package provider

import (
"time"
)

// Provider is an auth provider
type Provider interface {
// String returns the name of the provider
String() string
// Options returns the options of a provider
Options() Options
// Endpoint for the provider
Endpoint() string
// Redirect url incase of UI
Redirect() string
}

// Grant is a granted authorisation
type Grant struct {
// token for reuse
Token string
// Expiry of the token
Expiry time.Time
// Scopes associated with grant
Scopes []string
}
Loading

0 comments on commit 9a7a65f

Please sign in to comment.