react-use-credential-management

React hook to leverage Credential Management API

Usage no npm install needed!

<script type="module">
  import reactUseCredentialManagement from 'https://cdn.skypack.dev/react-use-credential-management';
</script>

README

react-use-credential-management

React hook to leverage Credential Management API

Usage

Wrap your <App/> with AuthProvider:

const App: React.FC = () => {
  return (
    <AuthProvider authenticate={authenticate}>
      <div className="App">{...}</div>
    </AuthProvider>
  )
}

The authenticate prop must be an async function taking a PasswordCredentialData as first argument, and returning a complete user profile. Use it to fetch icon URL and full name from backend. Example:

const authenticate = async ({ id, password }: PasswordCredentialData) => {
  const body = new FormData()
  body.append('id', id)
  body.append('password', password)

  const url = new URL('/auth/login', 'https://example.com')
  const res = await fetch(url.href, { body, method: 'POST' })
  if (!res.ok) {
    throw new Error(res.statusText)
  }

  const { iconURL, name } = await res.json()
  return { id, password, iconURL, name }
}

Where id is whatever unique value you choose to identify users, be it email, username, or anything else. It will be used to retrieve unique user information from your database.

Login button

Use login to try stored credentials. Pass 'silent' to prevent asking the user for permission, 'optional' to optionally ask for consent if needed (otherwise, behaves as 'silent'), and 'required' to unconditionally ask for consent.

const { isLoading, login } = useCredentialManagement()

if (isLoading) {
  return null
}

return <button onClick={login('optional')}>Login</button>

Use logout to require user consent next login:

const { isLoading, logout } = useCredentialManagement()

if (isLoading) {
  return null
}

return <button onClick={logout}>Login</button>

Use isAuthenticated to check if user is already logged in. You can leverage it to select between login and logout buttons:

const {
  isAuthenticated,
  isLoading,
  logout,
  silentLogin,
} = useCredentialManagement()

if (isLoading) {
  return null
}

return isAuthenticated ? (
  <button onClick={logout}>Login</button>
) : (
  <button onClick={login('optional')}>Login</button>
)

Use signup to create new credentials:

const { isLoading, signup } = useCredentialManagement()
const [isFormOpen, setFormOpen] = useState(false)

if (isLoading) {
  return null
}

const onSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
  e.preventDefault()
  const { elements } = e.currentTarget

  const extract = (name: string) => {
    const el = elements.namedItem(name) as HTMLInputElement
    return el.value
  }
  await signup(extract('id'), extract('password'))
  setFormOpen(false)
}

return (
  <form onSubmit={onSubmit}>
    <p>
      <label>
        <span>Username:</span>
        <input name="id" autoComplete="username" required />
      </label>
    </p>

    <p>
      <label>
        <span>Password:</span>
        <input
          name="password"
          type="password"
          autoComplete="current-password"
          required
        />
      </label>
    </p>

    <button type="submit">Submit</button>
  </form>
)

Complete example:

const {
  isAuthenticated,
  isLoading,
  login,
  logout,
  signup,
} = useCredentialManagement()
const [isFormOpen, setFormOpen] = useState(false)

if (isLoading) {
  return null
}

const onSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
  e.preventDefault()
  const { elements } = e.currentTarget

  const extract = (name: string) => {
    const el = elements.namedItem(name) as HTMLInputElement
    return el.value
  }
  await signup(extract('id'), extract('password'))
  setFormOpen(false)
}

const silentLogin = async () => {
  const success = await login('optional')
  setFormOpen(!success)
}

return isAuthenticated ? (
  <button onClick={logout}>Logout</button>
) : isFormOpen ? (
  <form onSubmit={onSubmit}>
    <p>
      <label>
        <span>Username:</span>
        <input name="id" autoComplete="username" required />
      </label>
    </p>

    <p>
      <label>
        <span>Password:</span>
        <input
          name="password"
          type="password"
          autoComplete="current-password"
          required
        />
      </label>
    </p>

    <button type="submit">Submit</button>
  </form>
) : (
  <button onClick={silentLogin}>Login</button>
)

License

This project is licensed under the MIT License - see LICENSE for details.