import {useContext} from 'react'
import {ApiContext} from "../../services";
import {useOidc} from "./useOidc";
/**
* @typedef {object} ScopeCheckReturn
*/
type ScopeCheckReturn = {
/**
* Whether the logged in user has ALL of the provided scopes
*/
allScope: boolean
/**
* Whether the logged in user has ONE OR MORE of the provided scopes
*/
anyScope: boolean
/**
* Whether there is a user currently logged in (if no user is present, all return values are false)
*/
loggedIn: boolean
}
/**
* Custom hook for checking whether an OIDC user has the provided scopes.
*
* The hook can accept zero or more strings that represent the requested
* scopes, and returns a set of booleans representing the results.
*
* The three returned booleans are: `loggedIn`, `anyScope`, `allScope`
* - `loggedIn`: Whether there is a user currently logged in (if no user is present, all return values are false)
* - `anyScope`: Whether the logged in user has ONE OR MORE of the provided scopes
* - `allScope`: Whether the logged in user has ALL of the provided scopes
*
* The hook uses the {@link ApiManager}'s templating engine to match provided scopes to the current environment.
* This means you can provide generic scopes (e.g. an API interface scope) and it will templated to the
* correct environment equivalent.
*
*
* ### Hook Usage
*
* #### Checking if a user has a specific scope
* ```
* const {loggedIn, anyScope, allScope} = useScopeCheck('scope1')
* ```
* A single scope string can be checked against the scopes of the logged in user.
* In this use case `anyScope` and `allScope` are equivalent, a single scope will be true for both.
*
* In practice, you will probably pull a scope from your {@link ApiInterface}
* subclass, and capture just the boolean result you're interested in. You can
* even rename your boolean as part of the destructure:
* ```
* const {allScope: isAdmin} = useScopeCheck(myApiInterface.scopes.ADMIN)
* ```
*
* #### Checking if a user has multiple scopes
* ```
* const {loggedIn, anyScope, allScope} = useScopeCheck(['scope1','scope2'])
* ```
* Pass an array of scope strings to check multiple scopes at once. In this
* case `anyScope` will be true if the user has *at least one* of the provided
* scopes, and `allScope` will be true if the user has *every one* of the
* provided scopes.
*
* An empty scopes array will result in `false`s in `allScope` and `anyScope`.
*
* #### Checking if a user is logged in
* ```
* const {loggedIn} = useScopeCheck()
* ```
* The hook can also be called without any arguments. Both `allScope` and
* `anyScope` will be false, but `loggedIn` is valid, so you can use this to
* check that a user has logged in without checking any specific scopes.
*
* @category Hooks
*
* @param [scopes] {string | string[]} The scope(s) to check against the current user
* @return {ScopeCheckReturn} The results of checking the scope(s)
*/
export function useScopeCheck(scopes: string | string[] | undefined): ScopeCheckReturn {
// Load oidcUser in case of scope props
let {user} = useOidc()
// Load apiManager for scope templating
let apiContext = useContext(ApiContext)
let allScope = false
let anyScope = false
let loggedIn = false
// Check if their is a user logged in
if (user) {
loggedIn = true
// If no scopes are provided, return the loggedIn value
if (scopes !== undefined) {
// Load scopes from the user
let {scope: returnedScopeString} = user
let returnedScopes = returnedScopeString.split(' ')
if (Array.isArray(scopes)) {
// Have to set allScope to true for the logic below to work
// (the length check keeps allScope false if scopes is an empty array)
allScope = scopes.length > 0
for (let specificScope of scopes) {
// Convert generic scope into templated scope, if in template format
// Leaves un-templated scopes unchanged
let templatedScope = apiContext.getTemplatedScope(specificScope)
allScope = allScope && returnedScopes.includes(templatedScope)
anyScope = anyScope || returnedScopes.includes(templatedScope)
}
} else {
// Convert generic scope into templated scope, if in template format
// Leaves un-templated scopes unchanged
let templatedScope = apiContext.getTemplatedScope(scopes)
allScope = returnedScopes.includes(templatedScope)
anyScope = returnedScopes.includes(templatedScope)
}
}
}
return {allScope, anyScope, loggedIn}
}
Source