@simpleview/sv-auth-client

6.0.0 • Public • Published

sv-auth-client

Client for communicating with sv-auth. This npm package contains classes and helpers for communicating with the sv-auth GraphQL system.

Use cases include:

  • Registering permissions on the auth system for your product.
  • Working with tokens from the auth system.
  • Wrapping the UI of your product behind the auth system.
  • Converting a token into a user and checking their permissions.

Changelog - See the latest changes to sv-auth-client.

OS Support

The expectation is that this application will be installed in Linux using sv-kubernetes.

Prerequisites

Installation

Using npm:

npm install @simpleview/sv-auth-client

Using yarn:

yarn install @simpleview/sv-redirects-client

Update

To update to the latest version, just rerun the install command.

Setup

For integrating your project with auth, please see the Setup Instructions.

Implementation & Usage

AuthClient

The AuthClient class is for communicating with the authentication system which provides some caching and ease of use for working with User objects.

  • args
    • graphUrl - The URL of the GraphQL server that you wish to communicate with. Most implementations should likely point to the live GraphQL endpoint at https://graphql.simpleviewinc.com/.
    • cacheDuration - The duration of how long cache entries should remain in the AuthClient cache. Defaults to 1 hour, generally you should not pass this setting unless unit testing.

AuthClient.getUser

This method wraps the call to auth.users_current in a caching layer to ensure that it's performant and is properly updating if a user's permissions have changed.

Generally you will want to make this call very early in your GraphQL stack in order to make the user available on context for all calls to access.

  • args
    • token - string - The jwt token retrieved from the auth system.
    • acct_id - string - The acct_id the user is accessing.
    • headers - object - HTTP headers, x-sv-permissionjson can be used for overwriting user permissions in unit tests.

Returns auth_user.

const user = authClient.getUser({
	token,
	acct_id : "0"
});

getTokenFromHeaders

Extracts the token from the authorization header.

const { getTokenFromHeaders } = require("@simpleview/sv-auth-client");
const server = new ApolloServer({
	...
	context: ({ req }) => {
		return {
			token : getTokenFromHeaders(req.headers)
		};
	}
});

AdminPrefix

AdminPrefix can be loaded into the sv-graphql-client GraphServer to use as a client library for accessing admin in GraphQL.

const { AdminPrefix } = require("@simpleview/sv-auth-client");
const { GraphServer } = require("@simpleview/sv-graphql-client");
const graphServer = new GraphServer({ graphUrl : GRAPH_URL, prefixes : [AdminPrefix] });

AuthPrefix

AdminPrefix can be loaded into the sv-graphql-client GraphServer to use as a client library for accessing auth in GraphQL.

const { AuthPrefix } = require("@simpleview/sv-auth-client");
const { GraphServer } = require("@simpleview/sv-graphql-client");
const graphServer = new GraphServer({ graphUrl : GRAPH_URL, prefixes : [AuthPrefix] });

canIds(perm, node_type, bindings)

This function is identical to User.canIds but requires you to pass in the bindings manually, it should only be used in cases where you cannot use DirectiveCheckPerm. See the documentation for User.canIds for how it functions.

DirectiveGetUser

DirectiveGetUser can be used to convert a token into a user session.

When using the directive, if your system passes the request headers object on context, you can utilize the header x-sv-permissionjson to set the permissions for a specific request. This can only be used if the token is sv : true and it reduces the permissions of that specific request. This makes unit tests easier as you don't need to create roles and users for each specific cross section of permission testing.

Adding DirectiveGetUser

The recommended approach is to split out a separate file in your schema to include the directive.

Note: You will need to provide name, which should be PREFIX_getUser and graphUrl which should point to the version of auth you are accessing.

const {
	getDirectiveGetUser
} = require("@simpleview/sv-auth-client");

module.exports = getDirectiveGetUser({ name : NAME, graphUrl : AUTH_URL })

// example
module.exports = getDirectiveGetUser({ name : "redirects_checkPerm", graphUrl : "https://graphql.simpleviewinc.com/link/auth-v2/" })

If you are not using schemaLoader, getDirectiveGetUser() returns { schemaDirectives, typeDefs } and you can manually integrate those with your existing directives and schema files.

Using DirectiveGetUser

The directive is usually best applied to your root resolver, and requires that your root resolver has an acct_id as a top-level filter parameter. This is best used in conjunction with the checkPerm resolver to enforce the permissions on that user.

In the below example, the top-level prefix converts the token into a user, then the specific resolver enforces permissions based on that user.

query {
	prefix(acct_id: String): prefix_query @prefix_getUser

	type prefix_query {
		some_endpoint: some_result @prefix_checkPerm(sv: true)
	}
}

DirectiveCheckPerm

DirectiveCheckPerm can be used to enforce permissions to access a resolver. It can enforce permissions based on sv, permissions or retrieve object_bindings.

Adding the Directive

The recommended approach is to split out a separate file in your schema to include the directive.

Note: You will need to provide name, which should be PREFIX_checkPerm and graphUrl which should point to the version of auth you are accessing.

const {
	getDirectiveCheckPerm
} = require("@simpleview/sv-auth-client");

module.exports = getDirectiveCheckPerm({ name : NAME, graphUrl : AUTH_URL });

// example
module.exports = getDirectiveCheckPerm({ name : "redirects_checkPerm", graphUrl : "https://graphql.simpleviewinc.com/link/auth-v2/" });

If you are not using schemaLoader, getDirectiveGetUser() returns { schemaDirectives, typeDefs } and you can manually integrate those with your existing directives and schema files.

Using the directive

  • perms - [String] - An array of permissions this endpoint requires.
  • sv - Boolean - Whether this endpoint requires an SV user.
  • bindings
    • perms - [String] - The permissions for bindings to return.
    • node_types - [String] - The node_types to return binding information on.
query {
	# require SV user
	some_query: some_result @prefix_checkPerm(sv: true)
	# require permission
	some_query: some_result @prefix_checkPerm(perms: ["foo"])
	# require multiple permissions
	some_query: some_result @prefix_checkPerm(perms: ["foo", "bar"])
	# pull in binding information
	some_query: some_result @prefix_checkPerm(bindings: { node_types : ["dms.accounts"] })
	some_query: some_result @prefix_checkPerm(bindings: { perms : ["dms.accounts.read", "dms.accounts.write"] })
	some_query: some_result @prefix_checkPerm(bindings: { node_types : ["dms.accounts", "dms.groups"], perms : ["dms.accounts.read", "dms.accounts.write", "dms.accounts.remove"] })
}

User

User is returned by AuthClient.getUser but it can also be used without AuthClient when you want to convert the return from auth.current into a user which you wish to check permissions on.

const { User } = require("@simpleview/sv-auth-client");
const result = await graphServer.auth.current({
	acct_id : "0",
	fields : `
		success
		message
		doc {
			permissionJson
		}
	`
});
const user = new User(result.doc);
user.can(["some.perm.name"]);

User.can

  • perms - array of strings - Returns boolean as to whether the user has all requested permissions.
const allow = user.can(["cms.something.another", "cms.another.permission"]);

User.canIds

This function is used for assisting in filtering down queries based on what IDs a user has access to for a given permission and node_type. In order to use this function you must specify bindings in the DirectiveCheckPerm for the GraphQL endpoint being executed. If you are not using that mechanic, then use the generic canIds function rather than the User.canIds variant.

Returns true if the user has root access to this permission/node_type.

Returns false if the user lacks all access to this permission/node_type. Note, that lacking access to this permission does not mean the user cannot see any of the content type, as they may have permissions to a node_type higher in the hierarchy.

Returns string[] of ids if the user has object bindings.

  • perm - string - Name of the permission.
  • node_type - string - Name of the node_type to return the ids for.
const account_ids = user.canIds("dms.accounts.read", "dms.account");
const group_ids = user.canIds("dms.accounts.read", "dms.group");

const result = await sqlQuery(`
	SELECT * FROM accounts a
	WHERE
		(
			@account_ids is null OR account_id IN (@account_ids)
			OR
			@group_ids is null OR group_id IN (@group_ids)
		)
`, {
	account_ids : account_ids === true ? null : account_ids,
	group_ids : group_ids === true ? null : group_ids
})

User.toPlain

Convert the User object back to a plain JS object.

OAuth 2.0

This is a library of functions to enable a product application to authenticate a user using the OAuth 2.0 framework. This works by handshaking codes and challenge tokens between the product and auth and uses the auth login to generate the access tokens for the product. There is a separate class for Express but Express is not required to use OAuth 2.0 authentication.

  • User visits product application page
  • If there is a session with a token and refresh token then serves content
  • Creates state random string and stores in session
  • Creates code_verifier random string and stores in session
  • Redirects to auth /oauth2/login/ endpoint passing:
    • response_type - must be "code"
    • code_challenge_method - must be "S256"
    • client_id - The client_id of the application. e.g. admin or cms. This id should be registered with auth as an oauth_client.
    • redirect_uri - The url which auth will redirect to upon successful login or if already logged in. This should match the redirect_uri registered with auth as an oauth_client. e.g. https://test-account.admin.simpleviewinc.com/oauth2/callback/.
    • state - Generated above
    • code_challenge - Base64URLEncode(SHA256(code_verifier))
    • sv_auth_params - JSON string containing an object. The properties of this object are added to the auth login url as extra parameters. This can be used to add e.g. the account_name or the product.
  • If not logged in then redirects to auth login with redirectUrl of /oauth2/login/
  • If logged in:
    • Stores client_id, code_challenge, date, refresh_token and code which is random generated string
    • Redirects to passed in redirect_uri passing back code and state
  • Application callback page verifies state in query is same as state stored in session
  • Calls auth /oauth2/token/ endpoint as a POST x-www-form-urlencoded with form data:
    • grant_type - Must be "authorization_code"
    • client_id - The client_id of the application. e.g. admin or cms. This id should be registered with auth as an oauth_client.
    • redirect_uri - The url which auth will redirect to upon successful login or if already logged in. This should match the redirect_uri registered with auth as an oauth_client. e.g. https://test-account.admin.simpleviewinc.com/oauth2/callback/.
    • code - As passed in query
    • code_verifier - As stored in session
  • An access_token and refresh_token are returned which should be stored in the client-side session
  • The user is then redirected back to the original product application page

To proxy requests to GraphQL including a user's token:

  • User visits product application page which runs a query
  • A POST request occurs to e.g. https://client.simpleviewapp.com/graphql/ for some query
  • Application should validate is POST and if origin and/or referer headers exist they match the expected domain
  • If the access_token is nearing expiration:
    • POST x-www-form-urlencoded request to auth /oauth2/token/ route with form data:
    • grant_type - Must be "refresh_token"
    • refresh_token - This should be a valid refresh token previously obtained from auth
    • An access_token and refresh_token are returned which should be stored in the client-side session
  • Proxy request to upstream graphql.simpleviewinc.com adding users access_token from client-side session as header authorization Bearer

The functions outlined below ease the burden of the flows described above. For Express there is a separate class which significantly eases implementation. It is the responsibility of the product application to create a valid redirect uri such as /oauth2/callback/ in their application as well as register the client as an oauth_client.

oauth2CreateRandomKey

This function creates a random key for use as the state or code_verifier.

This function returns a string and has no parameters.

oauth2CreateKeyHash

This function creates a hash of the passed in string. This can be used to create a code_challenge from the code_verifier for passing to the auth login.

oauth2CreateLoginUrl

This function creates a login url with the required OAuth 2.0 parameters.

Returns a url to /oauth2/login for auth.

  • args
    • authUrl - optional string - The URL of the auth system the website should utilise. Defaults to the live auth system. If you need to reference another system you can pass it here. Usually only used when testing in non-production environments.
    • client_id - string - The client_id of the application. e.g. admin or cms. This id should be registered with auth as an oauth_client.
    • redirect_uri - string - The url which auth will redirect to upon successful login or if already logged in. This should match the redirect_uri registered with auth as an oauth_client. e.g. https://test-account.admin.simpleviewinc.com/oauth2/callback/.
    • sv_auth_params - optional object - The properties of this object are added to the auth login url as extra parameters. This can be used to add e.g. the account_name or the product.
    • state - string - This is an opaque value used by the product application to maintain state between the request and callback. This value is included by auth when redirecting back to the product. This parameter should be used to prevent cross-site request forgery.
    • code_verifier - string - This is a random key which should be generated from oauth2CreateRandomKey. This key is hashed using oauth2CreateKeyHash and passed as a code_challenge.

oauth2CreateLogoutUrl

This function creates a logout url to log the user out of auth and then redirect back to the auth login.

  • args
    • authUrl - optional string - The URL of the auth system the website should utilise. Defaults to the live auth system. If you need to reference another system you can pass it here. Usually only used when testing in non-production environments.
    • redirect_uri - string - The url which auth will redirect to upon successful login. Usually this is the current url. e.g. https://test-account.admin.simpleviewinc.com/.
    • sv_auth_params - optional object - The properties of this object are added to the auth login url as extra parameters. This can be used to add e.g. the account_name or the product.

oauth2GetTokens

This function calls the /oauth2/token route on auth. This is used for authorising the code in the callback.

When authorising the code the route checks that the client_id and the redirect_uri are valid for the client. The code_verifier is then hashed and the new hash and code are then checked that we have a previously stored code and code_challenge which is still valid. This call must be made within five minutes of the original login call which stores the challenge.

Returns a token and refresh_token.

  • args
    • authUrl - optional string - The URL of the auth system the website should utilise. Defaults to the live auth system. If you need to reference another system you can pass it here. Usually only used when testing in non-production environments.
    • client_id - string - The client_id of the application. e.g. admin or cms. This id should be registered with auth as an oauth_client.
    • redirect_uri - string - The url which auth will redirect to upon successful login or if already logged in. This should match the redirect_uri registered with auth as an oauth_client. e.g. https://test-account.admin.simpleviewinc.com/oauth2/callback/.
    • code - string - This should have been previously passed from auth to the callback.
    • code_verifier - string - This is a random key which should be generated from oauth2CreateRandomKey. This should have been stored in the session.

oauth2GetTokensFromRefresh

This function calls the /oauth2/token route on auth. This is used for refreshing tokens.

When requesting new tokens the route checks the validity of the refresh_token.

Returns a token and refresh_token.

  • args
    • authUrl - optional string - The URL of the auth system the website should utilise. Defaults to the live auth system. If you need to reference another system you can pass it here. Usually only used when testing in non-production environments.
    • refresh_token - string - This should be a valid refresh token previously obtained from auth.

OAuth2Express

The OAuth2Express class provides a simple interface to allow products using Express to easily add OAuth 2.0 authentication with auth. See the sv-auth readme for sample usage.

  • args
    • authUrl - optional string - The URL of the auth system the website should utilise. Defaults to the live auth system. If you need to reference another system you can pass it here. Usually only used when testing in non-production environments.
    • client_id - string - The client_id of the application. e.g. admin or cms. This id should be registered with auth as an oauth_client.
    • callbackPath - optional string - The path that is registered as a callback route. Defaults to /oauth2/callback/.
    • logoutPath - optional string - The path that is registered as a logout route. Defaults to /logout/.
    • createParams - optional function - When executed with req as a parameter, returns an object containing any parameters which are to be passed to the auth login such as acct_id, account_name and product. Defaults to undefined which will pass no extra parameters.
    • sessionKey - optional string - Key to store the declared session. Defaults to session.

OAuth2Express.middleware

This method is an express middleware function designed to wrap any private routes. It takes the same parameters as an Express route (req, res, next).

If there is currently a user token in the session and it is older than a day, it will try to refresh the token.

If there is no user session then the user is redirected to the auth login.

If the above exceptions do not apply then the next method is run to move Express to the function for the route.

OAuth2Express.callback

This method must be registered on the callback route. It takes the same parameters as an Express route (req, res).

It obtains a token, refresh_token and user from auth, stores them in the session and then redirects the user back to the original path.

OAuth2Express.logout

This method must be registered on the logout route. It takes the same parameters as an Express route (req, res).

It deletes the session entry and redirects the user to the auth logout route, which logs the user out from auth and then redirects to the auth login.

OAuth2Express.applyMiddleware

This is a helper method which registers the callback and logout functions on the callbackPath and logoutPath respectively.

Development

  • Enter dev environment - sudo npm run docker
  • Test - npm test
  • Publish - sudo npm run publish SEMVER

Related Documentation

Troubleshooting

For any assistance please reach out on the sv-auth Slack channel.

Readme

Keywords

none

Package Sidebar

Install

npm i @simpleview/sv-auth-client

Weekly Downloads

227

Version

6.0.0

License

MIT

Unpacked Size

80.1 kB

Total Files

29

Last publish

Collaborators

  • georgeyb-sv
  • sloutrel
  • dlford
  • korychinn_sv
  • sv-mikael
  • actionawesome
  • arkmuntasser
  • leandrodalmassoglb
  • kristian.gonzalez
  • davidlpons
  • mauriciodeleonc
  • mpcarolin
  • csarantidessv
  • mikesan789
  • owenallenaz
  • qman33
  • svjoshua
  • mkes99
  • colin.booen
  • ssmith_sv
  • aglazebrook
  • rmaynes