iam

package module
v2.4.2 Latest Latest
Warning

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

Go to latest
Published: Feb 6, 2024 License: Apache-2.0 Imports: 26 Imported by: 3

README

Build Status

IAM Go SDK

This is AccelByte's IAM Go SDK for integrating with IAM in Go projects.

Usage

Importing package
import "github.com/AccelByte/iam-go-sdk/v2"
Creating default IAM client
cfg := &iam.Config{
    BaseURL: "<IAM URL>",
	BasicBaseURL: "<Basic URL>",
    ClientID: "<client ID>",
    ClientSecret: "<client secret>",
}

client := iam.NewDefaultClient(cfg)

It's recommended that you store the interface rather than the type since it enables you to mock the client during tests.

var client iam.Client

client := iam.NewDefaultClient(cfg)

So during tests, you can replace the client with:

var client iam.Client

client := iam.NewMockClient() // or create your own mock implementation that suits your test case

Note

By default, the client can only do token validation by requesting to IAM service.

To enable local validation, you need to call:

client.StartLocalValidation()

Then the client will automatically get JWK and revocation list and refreshing them periodically. This enables you to do local token validation and JWT claims parsing.

However, if you need to validate permission, you'll need to call ClientTokenGrant() to retrieve client access token that will be used as bearer token when requesting role details to IAM service.

Calling ClientTokenGrant() once will automatically trigger periodic token refresh.

client.ClientTokenGrant()
Validating token
Validating locally using downloaded JWK and revocation list:
claims, _ := client.ValidateAndParseClaims(accessToken)

Note

Store the claims output if you need to validate it's permission, role, or other properties.

Validating by sending request to IAM service:
ok, _ := client.ValidateAccessToken(accessToken)
Validating permission

For example, you have a resource permission that needs NAMESPACE:{namespace}:USER:{userId} resource string and 4 [UPDATE] action to access.

Using claims you can verify if the token owner is allowed to access the resource by:

permissionResource := make(map[string]string)
permissionResource["{namespace}"] = "example"
permissionResource["{userId}"] = "example"
client.ValidatePermission(claims, iam.Permission{Resource:"NAMESPACE:{namespace}:USER:{userId}", Action:4}, permissionResource)
Validating Audience

Validate audience from the token owner with client's base URI

_ = client.ValidateAudience(claims *JWTClaims) error

Note

Required client access token to get client information (client base URI)

Validating Scope

Validate scope from token owner with client scope

_ = client.ValidateScope(claims *JWTClaims, scope string) error
Health check

Whenever the IAM service went unhealthy, the client will know by detecting if any of the automated refresh goroutines has error.

You can check the health by:

client.HealthCheck()

Jaeger Tracing

IAM service client supports Opentracing Jaeger Traces in Zipkin B3 format(multiple headers mode). Additionally, the client handles k8s istio traces and includes it into outbound calls.

Jaeger Tracing configuration

To configure Jaegeer Client - provide Jaeger Agent host:port or Jaeger Collector URL and setup global tracer

/*
func InitGlobalTracer(
    jaegerAgentHost string,
    jaegerCollectorEndpoint string,
    serviceName string,
    realm string,
)
*/

jaeger.InitGlobalTracer(jaegerAgentHost, "", "service-name", "node-name")
// or
jaeger.InitGlobalTracer("", jaegerCollectorURL, "service-name", "node-name")
Jaeger Tracing usage

Use API methods with received from the response context

// istead of 
validationResult, err := testClient.ValidatePermission(
    claims,
    requiredPermission,
    permissionResources,
)

// use received from the request context
validationResult, err := testClient.ValidatePermission(
    claims,
    requiredPermission,
    permissionResources,
    WithJaegerContext(ctx),
)

// or an empty context to start a new Jaeger Span
validationResult, err := testClient.ValidatePermission(
    claims,
    requiredPermission,
    permissionResources,
    WithJaegerContext(context.Background()),
)

Documentation

Index

Constants

View Source
const (
	UserStatusEmailVerified = 1
	UserStatusPhoneVerified = 1 << 1
	UserStatusAnonymous     = 1 << 2
)

JFlags constants

View Source
const (
	MockUnauthorized = "unauthorized"
	MockForbidden    = "forbidden"
	MockAudience     = "http://example.com"
	MockSecret       = "mocksecret"
)

Mock IAM constants

View Source
const (
	ActionCreate = 1
	ActionRead   = 1 << 1
	ActionUpdate = 1 << 2
	ActionDelete = 1 << 3
)

Permission action bit flags

View Source
const (
	NamespaceTypeGame = "Game"
)

Variables

View Source
var ErrNamespaceNotFound = errors.New("namespace not found")

Functions

This section is empty.

Types

type Client

type Client interface {
	// ClientTokenGrant starts client token grant to get client bearer token for role caching
	ClientTokenGrant(opts ...Option) error

	// ClientToken returns client access token
	ClientToken(opts ...Option) string

	// DelegateToken
	DelegateToken(extendNamespace string, opts ...Option) (string, error)

	// StartLocalValidation starts goroutines to refresh JWK and revocation list periodically
	// this enables local token validation
	StartLocalValidation(opts ...Option) error

	// ValidateAccessToken validates access token by calling IAM service
	ValidateAccessToken(accessToken string, opts ...Option) (bool, error)

	// ValidateAndParseClaims validates access token locally and returns the JWT claims contained in the token
	ValidateAndParseClaims(accessToken string, opts ...Option) (*JWTClaims, error)

	// ValidatePermission validates if an access token has right for a specific permission
	// requiredPermission: permission to access resource, example:
	// 		{Resource: "NAMESPACE:{namespace}:USER:{userId}", Action: 2}
	// permissionResources: resource string to replace the `{}` placeholder in
	// 		`requiredPermission`, example: p["{namespace}"] = "accelbyte"
	ValidatePermission(claims *JWTClaims, requiredPermission Permission,
		permissionResources map[string]string, opts ...Option) (bool, error)

	// ValidateRole validates if an access token has a specific role
	ValidateRole(requiredRoleID string, claims *JWTClaims, opts ...Option) (bool, error)

	// UserPhoneVerificationStatus gets user phone verification status on access token
	UserPhoneVerificationStatus(claims *JWTClaims, opts ...Option) (bool, error)

	// UserEmailVerificationStatus gets user email verification status on access token
	UserEmailVerificationStatus(claims *JWTClaims, opts ...Option) (bool, error)

	// UserAnonymousStatus gets user anonymous status on access token
	UserAnonymousStatus(claims *JWTClaims, opts ...Option) (bool, error)

	// HasBan validates if certain ban exist
	HasBan(claims *JWTClaims, banType string, opts ...Option) bool

	// HealthCheck lets caller know the health of the IAM client
	HealthCheck(opts ...Option) bool

	// ValidateAudience validate audience of user access token
	ValidateAudience(claims *JWTClaims, opts ...Option) error

	// ValidateScope validate scope of user access token
	ValidateScope(claims *JWTClaims, scope string, opts ...Option) error

	// GetRolePermissions gets permissions of a role
	GetRolePermissions(roleID string, opts ...Option) (perms []Permission, err error)

	// GetClientInformation gets IAM client information,
	// it will look into cache first, if not found then fetch it to IAM.
	GetClientInformation(namespace string, clientID string, opts ...Option) (*ClientInformation, error)
}

Client provides interface for IAM Client It can be used as mocking point usage example:

func main() {
	config := Config{
		BaseURL:      "/iam",
		ClientID:     "clientID",
		ClientSecret: "clientSecret",
	}

	iamClient, _ := client.NewClient(&config)
	myFunction(iamClient)
}

func myFunction(iamClient *client.IAMClientAPI) {
	iamClient.ValidateTokenPermission(models.Permission{
		Resource: "NAMESPACE:{namespace}:EXAMPLE", Action: 4
		}, "accessToken")
}

func NewMockClient

func NewMockClient() Client

NewMockClient creates new mock IAM DefaultClient

type ClientInformation

type ClientInformation struct {
	ClientName  string `json:"clientName"`
	Namespace   string `json:"namespace"`
	RedirectURI string `json:"redirectUri"`
	BaseURI     string `json:"baseUri"`
}

ClientInformation holds client information

type Config

type Config struct {
	BaseURL                       string
	BasicBaseURL                  string
	ClientID                      string
	ClientSecret                  string
	RolesCacheExpirationTime      time.Duration // default: 60s
	JWKSRefreshInterval           time.Duration // default: 60s
	RevocationListRefreshInterval time.Duration // default: 60s
	Debug                         bool
}

Config contains IAM configurations

type DefaultClient

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

DefaultClient define oauth client config

func NewDefaultClient

func NewDefaultClient(config *Config) *DefaultClient

NewDefaultClient creates new IAM DefaultClient

func (*DefaultClient) ClientToken

func (client *DefaultClient) ClientToken(opts ...Option) string

ClientToken returns client access token

func (*DefaultClient) ClientTokenGrant

func (client *DefaultClient) ClientTokenGrant(opts ...Option) error

ClientTokenGrant starts client token grant to get client bearer token for role caching

func (*DefaultClient) DelegateToken added in v2.2.1

func (client *DefaultClient) DelegateToken(extendNamespace string, opts ...Option) (string, error)

func (*DefaultClient) GetClientInformation

func (client *DefaultClient) GetClientInformation(namespace string, clientID string, opts ...Option) (*ClientInformation, error)

GetClientInformation gets IAM client information, it will look into cache first, if not found then fetch it to IAM.

func (*DefaultClient) GetRolePermissions

func (client *DefaultClient) GetRolePermissions(roleID string, opts ...Option) (perms []Permission, err error)

GetRolePermissions gets permissions of a role

func (*DefaultClient) HasBan

func (client *DefaultClient) HasBan(claims *JWTClaims, banType string, opts ...Option) bool

HasBan validates if certain ban exist

func (*DefaultClient) HealthCheck

func (client *DefaultClient) HealthCheck(opts ...Option) bool

HealthCheck lets caller know the health of the IAM client

func (*DefaultClient) StartLocalValidation

func (client *DefaultClient) StartLocalValidation(opts ...Option) error

StartLocalValidation starts goroutines to refresh JWK and revocation list periodically this enables local token validation

func (*DefaultClient) UserAnonymousStatus

func (client *DefaultClient) UserAnonymousStatus(claims *JWTClaims, opts ...Option) (bool, error)

UserAnonymousStatus gets user anonymous status on access token

func (*DefaultClient) UserEmailVerificationStatus

func (client *DefaultClient) UserEmailVerificationStatus(claims *JWTClaims, opts ...Option) (bool, error)

UserEmailVerificationStatus gets user email verification status on access token

func (*DefaultClient) UserPhoneVerificationStatus

func (client *DefaultClient) UserPhoneVerificationStatus(claims *JWTClaims, opts ...Option) (bool, error)

UserPhoneVerificationStatus gets user phone verification status on access token

func (*DefaultClient) ValidateAccessToken

func (client *DefaultClient) ValidateAccessToken(accessToken string, opts ...Option) (bool, error)

ValidateAccessToken validates access token by calling IAM service

func (*DefaultClient) ValidateAndParseClaims

func (client *DefaultClient) ValidateAndParseClaims(accessToken string, opts ...Option) (*JWTClaims, error)

ValidateAndParseClaims validates access token locally and returns the JWT claims contained in the token

func (*DefaultClient) ValidateAudience

func (client *DefaultClient) ValidateAudience(claims *JWTClaims, opts ...Option) error

ValidateAudience validate audience of user access token nolint: funlen

func (*DefaultClient) ValidatePermission

func (client *DefaultClient) ValidatePermission(claims *JWTClaims,
	requiredPermission Permission, permissionResources map[string]string, opts ...Option) (bool, error)

ValidatePermission validates if an access token has right for a specific permission requiredPermission: permission to access resource, example:

{Resource: "NAMESPACE:{namespace}:USER:{userId}", Action: 2}

permissionResources: resource string to replace the `{}` placeholder in

`requiredPermission`, example: p["{namespace}"] = "accelbyte"

nolint: funlen

func (*DefaultClient) ValidateRole

func (client *DefaultClient) ValidateRole(requiredRoleID string, claims *JWTClaims, opts ...Option) (bool, error)

ValidateRole validates if an access token has a specific role

func (*DefaultClient) ValidateScope

func (client *DefaultClient) ValidateScope(claims *JWTClaims, reqScope string, opts ...Option) error

ValidateScope validate scope of user access token

type ErrorRes added in v2.3.0

type ErrorRes struct {
	ErrorCode int64 `json:"errorCode"`
}

type HTTPClient

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPClient is an interface for http.Client. The purpose for having this so we could easily mock the HTTP call.

type JWK

type JWK struct {
	Kty string `json:"kty"`
	Use string `json:"use"`
	Kid string `json:"kid"`
	N   string `json:"n"`
	E   string `json:"e"`
}

JWK contains json web key's data

type JWTBan

type JWTBan struct {
	Ban     string    `json:"Ban"`
	EndDate time.Time `json:"EndDate"`
}

JWTBan holds information about ban record in JWT

type JWTClaims

type JWTClaims struct {
	Namespace            string          `json:"namespace"`
	DisplayName          string          `json:"display_name"`
	Roles                []string        `json:"roles"`
	NamespaceRoles       []NamespaceRole `json:"namespace_roles"`
	Permissions          []Permission    `json:"permissions"`
	Bans                 []JWTBan        `json:"bans"`
	JusticeFlags         int             `json:"jflgs"`
	Scope                string          `json:"scope"`
	Country              string          `json:"country"`
	ClientID             string          `json:"client_id"`
	IsComply             bool            `json:"is_comply"`
	ParentNamespace      string          `json:"parent_namespace,omitempty"`
	IssuedPlatformFrom   string          `json:"ipf,omitempty"`
	IssuedPlatformOn     string          `json:"ipo,omitempty"`
	SimultaneousPlatform string          `json:"sp,omitempty"`
	UnionID              string          `json:"union_id,omitempty"`
	UnionNamespace       string          `json:"union_namespace,omitempty"`
	ExtendNamespace      string          `json:"extend_namespace,omitempty"`
	jwt.Claims
}

JWTClaims holds data stored in a JWT access token with additional Justice Flags field

func (*JWTClaims) Validate

func (c *JWTClaims) Validate() error

Validate checks if the JWT is still valid

type Keys

type Keys struct {
	Keys []JWK `json:"keys"`
}

Keys contains json web keys

type MockClient

type MockClient struct {
	Healthy     bool   // set this to false to mock unhealthy IAM service
	RedirectURI string // set this to use custom redirectURI
}

MockClient define mock oauth client config

func (*MockClient) ClientToken

func (client *MockClient) ClientToken(opts ...Option) string

ClientToken returns client access token

func (*MockClient) ClientTokenGrant

func (client *MockClient) ClientTokenGrant(opts ...Option) error

ClientTokenGrant starts client token grant to get client bearer token for role caching

func (*MockClient) DelegateToken added in v2.2.1

func (client *MockClient) DelegateToken(extendNamespace string, opts ...Option) (string, error)

func (*MockClient) GetClientInformation

func (client *MockClient) GetClientInformation(namespace string, clientID string, opts ...Option) (clientInfo *ClientInformation, err error)

GetClientInformation gets IAM client information

func (*MockClient) GetRolePermissions

func (client *MockClient) GetRolePermissions(roleID string, opts ...Option) (perms []Permission, err error)

GetRolePermissions gets permissions of a role

func (*MockClient) HasBan

func (client *MockClient) HasBan(claims *JWTClaims, banType string, opts ...Option) bool

HasBan validates if certain ban exist

func (*MockClient) HealthCheck

func (client *MockClient) HealthCheck(opts ...Option) bool

HealthCheck lets caller know the health of the IAM client

func (*MockClient) StartLocalValidation

func (client *MockClient) StartLocalValidation(opts ...Option) error

StartLocalValidation starts goroutines to refresh JWK and revocation list periodically this enables local token validation

func (*MockClient) UserAnonymousStatus

func (client *MockClient) UserAnonymousStatus(claims *JWTClaims, opts ...Option) (bool, error)

UserAnonymousStatus gets user anonymous status on access token

func (*MockClient) UserEmailVerificationStatus

func (client *MockClient) UserEmailVerificationStatus(claims *JWTClaims, opts ...Option) (bool, error)

UserEmailVerificationStatus gets user email verification status on access token

func (*MockClient) UserPhoneVerificationStatus

func (client *MockClient) UserPhoneVerificationStatus(claims *JWTClaims, opts ...Option) (bool, error)

UserPhoneVerificationStatus gets user phone verification status on access token

func (*MockClient) ValidateAccessToken

func (client *MockClient) ValidateAccessToken(accessToken string, opts ...Option) (bool, error)

ValidateAccessToken validates access token by calling IAM service

func (*MockClient) ValidateAndParseClaims

func (client *MockClient) ValidateAndParseClaims(accessToken string, opts ...Option) (*JWTClaims, error)

ValidateAndParseClaims validates access token locally and returns the JWT claims contained in the token

func (*MockClient) ValidateAudience

func (client *MockClient) ValidateAudience(claims *JWTClaims, opts ...Option) error

ValidateAudience gets user anonymous status on access token

func (*MockClient) ValidatePermission

func (client *MockClient) ValidatePermission(claims *JWTClaims,
	requiredPermission Permission, permissionResources map[string]string, opts ...Option) (bool, error)

ValidatePermission validates if an access token has right for a specific permission requiredPermission: permission to access resource, example:

{Resource: "NAMESPACE:{namespace}:USER:{userId}", Action: 2}

permissionResources: resource string to replace the `{}` placeholder in

`requiredPermission`, example: p["{namespace}"] = "accelbyte"

func (*MockClient) ValidateRole

func (client *MockClient) ValidateRole(requiredRoleID string, claims *JWTClaims, opts ...Option) (bool, error)

ValidateRole validates if an access token has a specific role

func (*MockClient) ValidateScope

func (client *MockClient) ValidateScope(claims *JWTClaims, scope string, opts ...Option) error

ValidateScope gets user anonymous status on access token

type NamespaceContext added in v2.3.0

type NamespaceContext struct {
	NotFound        bool   `json:"-"`
	Type            string `json:"type"`
	StudioNamespace string `json:"studioNamespace"`
}

type NamespaceRole

type NamespaceRole struct {
	RoleID    string `json:"roleId"`
	Namespace string `json:"namespace"`
}

type Option

type Option func(*Options)

func WithJaegerContext

func WithJaegerContext(ctx context.Context) Option

type Options

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

type Permission

type Permission struct {
	Resource        string   `json:"Resource"`
	Action          int      `json:"Action"`
	ScheduledAction int      `json:"schedAction,omitempty"`
	CronSchedule    string   `json:"schedCron,omitempty"`
	RangeSchedule   []string `json:"schedRange,omitempty"`
}

func (Permission) IsScheduled

func (perm Permission) IsScheduled() bool

IsScheduled checks if the schedule is active at current time

type RevocationList

type RevocationList struct {
	RevokedTokens bloom.FilterJSON           `json:"revoked_tokens"`
	RevokedUsers  []UserRevocationListRecord `json:"revoked_users"`
}

RevocationList contains revoked user and token

type Role

type Role struct {
	IsWildcard  bool         `json:"isWildcard"`
	AdminRole   bool         `json:"adminRole"`
	RoleID      string       `json:"roleId"`
	RoleName    string       `json:"roleName"`
	Permissions []Permission `json:"permissions"`
}

type TokenResponse

type TokenResponse struct {
	AccessToken           string          `json:"access_token"`
	RefreshToken          string          `json:"refresh_token"`
	ExpiresIn             int             `json:"expires_in"`
	TokenType             string          `json:"token_type"`
	Roles                 []string        `json:"roles"`
	AcceptedPolicyVersion []string        `json:"accepted_policy_version"`
	NamespaceRoles        []NamespaceRole `json:"namespace_roles"`
	Permissions           []Permission    `json:"permissions"`
	Bans                  []JWTBan        `json:"bans"`
	UserID                string          `json:"user_id"`
	PlatformID            string          `json:"platform_id,omitempty"`
	PlatformUserID        string          `json:"platform_user_id,omitempty"`
	JusticeFlags          int             `json:"jflgs,omitempty"`
	DisplayName           string          `json:"display_name"`
	Namespace             string          `json:"namespace"`
	IsComply              bool            `json:"is_comply"`
}

TokenResponse is the data structure for the response on successful token request.

type UserRevocationListRecord

type UserRevocationListRecord struct {
	ID        string    `json:"id" bson:"id"`
	RevokedAt time.Time `json:"revoked_at" bson:"revoked_at"`
}

UserRevocationListRecord is used to store revoked user data

Jump to

Keyboard shortcuts

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