iam

package module
v2.5.1 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 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

Import package
import "github.com/AccelByte/iam-go-sdk/v2"
Create 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)

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 the IAM service.

To enable local validation, you need to call:

client.StartLocalValidation()

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

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

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

client.ClientTokenGrant()
Validate token
Validate 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.

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

As an example, assume 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 with:

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

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

_ = client.ValidateAudience(claims *JWTClaims) error

Note

A client access token is required to get client information (client base URI).

Validate scope

Validate scope from the token owner with the client scope:

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

Whenever the IAM service is unhealthy, the client will know by detecting if any of the automated refresh goroutines have an error.

You can check the health with:

client.HealthCheck()

Jaeger tracing

The IAM service client supports Opentracing Jaeger Traces in Zipkin B3 format (multiple headers mode). Additionally, the client handles K8s Istio traces and includes them into outbound calls.

Configure Jaeger tracing

To configure the Jaegeer client, provide the Jaeger Agent host:port or the Jaeger Collector URL and set up the 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 received from the response context:

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

// use what's 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 (
	DefaultUserRoleID = "2251438839e948d783ec0e5281daf05b"
)
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) GetRoleNamespacePermission added in v2.5.0

func (client *DefaultClient) GetRoleNamespacePermission(namespace, roleID, requestNamespace string, rootSpan opentracing.Span) ([]Permission, error)

nolint: funlen, dupl

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 RoleNamespacePermission added in v2.5.0

type RoleNamespacePermission struct {
	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