@simpleview/sv-auth-client
v6.7.0
Published
Client for communicating with sv-auth
Downloads
137
Maintainers
Keywords
Readme
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
- Theclient_id
of the application. e.g.admin
orcms
. This id should be registered with auth as anoauth_client
.redirect_uri
- The url which auth will redirect to upon successful login or if already logged in. This should match theredirect_uri
registered with auth as anoauth_client
. e.g.https://test-account.admin.simpleviewinc.com/oauth2/callback/
.state
- Generated abovecode_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. theaccount_name
or theproduct
.
- 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
andcode
which is random generated string - Redirects to passed in
redirect_uri
passing backcode
andstate
- Stores
- Application callback page verifies
state
in query is same asstate
stored in session - Calls auth
/oauth2/token/
endpoint as aPOST x-www-form-urlencoded
with form data:grant_type
- Must be "authorization_code"client_id
- Theclient_id
of the application. e.g.admin
orcms
. This id should be registered with auth as anoauth_client
.redirect_uri
- The url which auth will redirect to upon successful login or if already logged in. This should match theredirect_uri
registered with auth as anoauth_client
. e.g.https://test-account.admin.simpleviewinc.com/oauth2/callback/
.code
- As passed in querycode_verifier
- As stored in session
- An
access_token
andrefresh_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
andrefresh_token
are returned which should be stored in the client-side session
- Proxy request to upstream
graphql.simpleviewinc.com
adding usersaccess_token
from client-side session asheader 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 - Theclient_id
of the application. e.g.admin
orcms
. This id should be registered with auth as anoauth_client
.redirect_uri
- string - The url which auth will redirect to upon successful login or if already logged in. This should match theredirect_uri
registered with auth as anoauth_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. theaccount_name
or theproduct
.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 fromoauth2CreateRandomKey
. This key is hashed usingoauth2CreateKeyHash
and passed as acode_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. theaccount_name
or theproduct
.
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 - Theclient_id
of the application. e.g.admin
orcms
. This id should be registered with auth as anoauth_client
.redirect_uri
- string - The url which auth will redirect to upon successful login or if already logged in. This should match theredirect_uri
registered with auth as anoauth_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 fromoauth2CreateRandomKey
. 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 - Theclient_id
of the application. e.g.admin
orcms
. This id should be registered with auth as anoauth_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 withreq
as a parameter, returns an object containing any parameters which are to be passed to the auth login such asacct_id
,account_name
andproduct
. Defaults toundefined
which will pass no extra parameters.sessionKey
- optional string - Key to store the declared session. Defaults tosession
.
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.