Source

tools/hooks/useScopeCheck.ts

import {useContext} from 'react'
import {useReactOidc} from "@axa-fr/react-oidc-context";
import {ApiContext} from "../../services";

/**
 * @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.
 *
 * #### Checking if a user has multiple scopes
 * ```
 * const {loggedIn, anyScope, allScope} = useScopeCheck(['scope1','scope2'])
 * ```
 * Multiple scope strings can be checked at once. In this use case `anyScope` will be true if the
 * user has at least one and `allScope` will only be true if the user has all the provided scopes.
 *
 * #### Checking if a user is logged in
 * ```
 * const {loggedIn} = useScopeCheck()
 * ```
 * The hook can also be called without any scopes being provided. This can be used to check if a
 * user is currently logged in or not.
 *
 * @category Hooks
 * @module useScopeCheck
 *
 * @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 {oidcUser} = useReactOidc()

    // 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 (oidcUser) {
        loggedIn = true

        // If no scopes are provided, return the loggedIn value
        if (scopes !== undefined) {
            // Load scopes from the user
            let {scope: returnedScopeString} = oidcUser
            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}
}