oauth

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 22, 2017 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package oauth is the OAuth2 authentication driver for github.com/hiendv/gate. It uses client implementations, not OAuth servers e.g. Google, Facebook, etc.

Example
var auth gate.Auth

roles := []fixtures.Role{
	{
		ID: fixtures.RandomString(8),
		Abilities: []fixtures.Ability{
			{Action: "GET", Object: "/api/v1/*"},
			{Action: "POST", Object: "/api/v1/users*"},
		},
	},
	{
		ID: fixtures.RandomString(8),
		Abilities: []fixtures.Ability{
			{Action: "GET", Object: "*"},
		},
	},
	{
		ID: fixtures.RandomString(8),
		Abilities: []fixtures.Ability{
			{Action: "POST", Object: "/api/v1/posts*"},
		},
	},
}

users := []fixtures.User{
	{
		ID:    "id",
		Email: "foo@local",
		Roles: []string{
			roles[0].ID,
			roles[1].ID,
		},
	},
	{
		ID:    "id2",
		Email: "bar@local",
		Roles: []string{},
	},
	{
		ID:    "id3",
		Email: "nobody@local",
		Roles: []string{},
	},
}

roleService := fixtures.NewMyRoleService(roles)
userService := fixtures.NewMyUserService(users)
tokenService := fixtures.NewMyTokenService(nil)

driver = New(
	NewGoogleConfig(
		gate.NewConfig("jwt-secret", "jwt-secret", time.Hour*1, false),
		"google-client-id",
		"google-client-secret",
		"http://localhost:8080",
	),
	GoogleStatelessHandler,
	dependency.NewContainer(userService, tokenService, roleService),
)

// Mocking
driver.setProvider(fixtures.OAuthProvider{
	map[string]gate.HasEmail{
		"code-token": GoogleUser{
			Email:         "foo@local",
			EmailVerified: true,
		},
	},
})

auth = driver
if auth == nil {
	fmt.Println("auth should not be nil")
	return
}

user, err := auth.Login(map[string]string{"code": "code"})
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("Tokens: %d\n", tokenService.Count())

jwt, err := auth.IssueJWT(user)
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("Tokens: %d\n", tokenService.Count())

parsedUser, err := auth.Authenticate(jwt.Value)
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%s:%s - %v\n", parsedUser.GetID(), parsedUser.GetEmail(), err)

err = auth.Authorize(parsedUser, "GET", "/api/v1/users")
fmt.Printf("%v\n", err)

err = auth.Authorize(parsedUser, "GET", "/api/v1/posts")
fmt.Printf("%v\n", err)

err = auth.Authorize(parsedUser, "POST", "/api/v1/users")
fmt.Printf("%v\n", err)

err = auth.Authorize(parsedUser, "POST", "/api/v1/posts")
fmt.Printf("%v\n", err)
Output:

Tokens: 0
Tokens: 1
id:foo@local - <nil>
<nil>
<nil>
<nil>
forbidden

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	gate.Config
	ClientID     string
	ClientSecret string
	Scopes       []string
	Endpoint     oauth2.Endpoint
	RedirectURI  string
	UserAPI      string
}

Config is the configuration for OAuth authentication

func NewFacebookConfig

func NewFacebookConfig(base gate.Config, id, secret, redirectURI string) Config

NewFacebookConfig is the constructor for OAuth configuration using Facebook API

func NewGoogleConfig

func NewGoogleConfig(base gate.Config, id, secret, redirectURI string) Config

NewGoogleConfig is the constructor for OAuth configuration using Google API

type DefaultProvider

type DefaultProvider struct {
	// contains filtered or unexported fields
}

DefaultProvider is the default OAuth provider

func (DefaultProvider) AuthCodeURL

func (provider DefaultProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string

AuthCodeURL returns a URL to OAuth 2.0 provider's consent page

func (DefaultProvider) Client

func (provider DefaultProvider) Client(ctx context.Context, t *oauth2.Token) internal.HTTPClient

Client returns an HTTP client using the provided token

func (DefaultProvider) Exchange

func (provider DefaultProvider) Exchange(ctx context.Context, code string) (*oauth2.Token, error)

Exchange converts an authorization code into a token

type Driver

type Driver struct {
	dependency.Container
	// contains filtered or unexported fields
}

Driver is OAuth authentication

func New

func New(config Config, handler LoginFunc, container dependency.Container) *Driver

New is the constructor for Driver

func (Driver) Authenticate

func (auth Driver) Authenticate(tokenString string) (user gate.User, err error)

Authenticate performs the authentication using JWT

Example
users := []fixtures.User{
	{
		ID:    "id",
		Email: "foo@local",
		Roles: []string{},
	},
}

userService := fixtures.NewMyUserService(users)
tokenService := fixtures.NewMyTokenService(nil)

driver := New(
	NewGoogleConfig(
		gate.NewConfig("jwt-secret", "jwt-secret", time.Hour*1, true),
		"google-client-id",
		"google-client-secret",
		"http://localhost:8080",
	),
	GoogleStatelessHandler,
	// Role service is omitted
	dependency.NewContainer(userService, tokenService, nil),
)
if driver == nil {
	fmt.Println("driver should not be nil")
	return
}

user, err := driver.Authenticate("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiaWQiLCJlbWFpbCI6ImZvb0Bsb2NhbCIsInJvbGVzIjpbXX0sImV4cCI6MTYwNTA1MjgwMCwianRpIjoiY2xhaW1zLWlkIiwiaWF0IjoxNjA1MDQ5MjAwfQ.GfDJ1Cl8f7nX6urWV4F2RgXGPXLfwYh9syfghVJ57XY")
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%s:%s - %v", user.GetID(), user.GetEmail(), err)
Output:

id:foo@local - <nil>

func (Driver) Authorize

func (auth Driver) Authorize(user gate.User, action, object string) (err error)

Authorize performs the authorization when a given user takes an action on an object using RBAC

func (Driver) GetUserAbilities

func (auth Driver) GetUserAbilities(user gate.User) (abilities []gate.UserAbility, err error)

GetUserAbilities returns a user's abilities

func (Driver) GetUserFromJWT

func (auth Driver) GetUserFromJWT(token gate.JWT) (user gate.User, err error)

GetUserFromJWT returns a user from a given JWT

func (Driver) IssueJWT

func (auth Driver) IssueJWT(user gate.User) (token gate.JWT, err error)

IssueJWT issues and stores a JWT for a specific user

Example
var auth gate.Auth

users := []fixtures.User{
	{
		ID:    "id",
		Email: "foo@local",
		Roles: []string{},
	},
}

userService := fixtures.NewMyUserService(users)
tokenService := fixtures.NewMyTokenService(nil)

driver = New(
	NewGoogleConfig(
		gate.NewConfig("jwt-secret", "jwt-secret", time.Hour*1, false),
		"google-client-id",
		"google-client-secret",
		"http://localhost:8080",
	),
	GoogleStatelessHandler,
	// Role service is omitted
	dependency.NewContainer(userService, tokenService, nil),
)

// Mocking
driver.setProvider(fixtures.OAuthProvider{
	map[string]gate.HasEmail{
		"code-token": GoogleUser{
			Email:         "foo@local",
			EmailVerified: true,
		},
	},
})

auth = driver
if auth == nil {
	fmt.Println("auth should not be nil")
	return
}

jwtConfig, err := gate.NewHMACJWTConfig("HS256", driver.config.JWTSigningKey(), driver.config.JWTExpiration(), driver.config.JWTSkipClaimsValidation())
if err != nil {
	fmt.Println(err)
	return
}

mockedJWTService := gate.NewJWTService(jwtConfig)
mockedJWTService.Now = func() time.Time {
	return time.Date(2020, time.November, 10, 23, 0, 0, 0, time.UTC)
}
mockedJWTService.GenerateClaimsID = func() string {
	return "claims-id"
}

driver.Container.SetJWTService(mockedJWTService)

user, err := auth.Login(map[string]string{"code": "code"})
if err != nil {
	fmt.Println(err)
	return
}

jwt, err := auth.IssueJWT(user)
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%s:%s@%s - %v", jwt.ID, jwt.Value, jwt.UserID, err)
Output:

claims-id:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiaWQiLCJlbWFpbCI6ImZvb0Bsb2NhbCIsInJvbGVzIjpbXX0sImV4cCI6MTYwNTA1MjgwMCwianRpIjoiY2xhaW1zLWlkIiwiaWF0IjoxNjA1MDQ5MjAwfQ.GfDJ1Cl8f7nX6urWV4F2RgXGPXLfwYh9syfghVJ57XY@id - <nil>

func (Driver) Login

func (auth Driver) Login(credentials map[string]string) (user gate.User, err error)

Login resolves OAuth authentication with the given handler and credentials

Example
var auth gate.Auth

users := []fixtures.User{
	{
		ID:    "id",
		Email: "foo@local",
		Roles: []string{},
	},
	{
		ID:    "id2",
		Email: "bar@local",
		Roles: []string{},
	},
}

userService := fixtures.NewMyUserService(users)

driver = New(
	NewGoogleConfig(
		gate.NewConfig("jwt-secret", "jwt-secret", time.Hour*1, false),
		"google-client-id",
		"google-client-secret",
		"http://localhost:8080",
	),
	GoogleStatelessHandler,
	// Token and Role services are omitted
	dependency.NewContainer(userService, nil, nil),
)

// Mocking
driver.setProvider(fixtures.OAuthProvider{
	map[string]gate.HasEmail{
		"code-token": GoogleUser{
			Email:         "foo@local",
			EmailVerified: true,
		},
		"code2-token": GoogleUser{
			Email:         "foo@local",
			EmailVerified: true,
		},
		"code3-token": GoogleUser{
			Email:         "bar@local",
			EmailVerified: true,
		},
	},
})

auth = driver
if auth == nil {
	fmt.Println("auth should not be nil")
	return
}

user, err := auth.Login(map[string]string{"code": "code"})
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%s:%s - %v\n", user.GetID(), user.GetEmail(), err)

secondUser, err := auth.Login(map[string]string{"code": "code2"})
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%s:%s - %v\n", secondUser.GetID(), secondUser.GetEmail(), err)

thirdUser, err := auth.Login(map[string]string{"code": "code3"})
if err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%s:%s - %v\n", thirdUser.GetID(), thirdUser.GetEmail(), err)
Output:

id:foo@local - <nil>
id:foo@local - <nil>
id2:bar@local - <nil>

func (Driver) LoginURL

func (auth Driver) LoginURL(state string) (string, error)

LoginURL returns the URL to the consent page

func (Driver) ParseJWT

func (auth Driver) ParseJWT(tokenString string) (token gate.JWT, err error)

ParseJWT parses a JWT string to a JWT

func (Driver) StoreJWT

func (auth Driver) StoreJWT(token gate.JWT) (err error)

StoreJWT stores a JWT using the given token service

type GoogleUser

type GoogleUser struct {
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
}

GoogleUser is the user from Google API

func (GoogleUser) GetEmail

func (user GoogleUser) GetEmail() string

GetEmail returns user's email

type LoginFunc

type LoginFunc func(driver Driver, code, state string) (gate.HasEmail, error)

LoginFunc is the handler of OAuth authentication

var GoogleStatelessHandler LoginFunc = func(driver Driver, code, state string) (account gate.HasEmail, err error) {

	token, err := driver.provider.Exchange(context.TODO(), code)
	if err != nil {
		return
	}

	client := driver.provider.Client(context.TODO(), token)
	if client == nil {
		err = errors.New("invalid API client")
		return
	}

	response, err := client.Get(driver.config.UserAPI)
	if err != nil {
		return
	}
	if response == nil {
		err = errors.New("invalid API response")
		return
	}
	defer func(response *http.Response) {
		e := response.Body.Close()
		if e == nil {
			return
		}

	}(response)

	var user GoogleUser
	err = json.NewDecoder(response.Body).Decode(&user)
	if err != nil {
		return
	}

	account = user
	return
}

GoogleStatelessHandler is the stateless login handler using Google API

var LoginFuncStub LoginFunc = func(Driver, string, string) (gate.HasEmail, error) {
	return nil, nil
}

LoginFuncStub is the stub for LoginFunc

type Provider

type Provider interface {
	AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
	Exchange(ctx context.Context, code string) (*oauth2.Token, error)
	Client(ctx context.Context, t *oauth2.Token) internal.HTTPClient
}

Provider is the OAuth provider

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL