Packages
An Elixir client for the Auth0 Management and Authentication APIs, built on Req.
Current section
Files
Jump to
Current section
Files
guides/users.md
# Managing users
Creating, finding and updating users, and moving them in and out in bulk. For roles and permissions see
[RBAC](rbac.md); for MFA enrolment see [MFA](mfa.md).
Assumes Management API credentials are configured — see [Configuration](configuration.md).
Auth0 reference: [Users](https://auth0.com/docs/api/management/v2/users),
[Jobs](https://auth0.com/docs/api/management/v2/jobs).
## The basics
```elixir
alias Auth0Client.Management.User
User.all(%{q: "email:*@example.com"})
#=> {:ok, [%{"user_id" => "auth0|abc123", ...}]}
User.get("auth0|abc123", %{fields: "email,name"})
#=> {:ok, %{"email" => "someone@example.com", "name" => "Someone"}}
User.create("Username-Password-Authentication", %{
email: "someone@example.com",
password: "a-strong-password"
})
User.update("auth0|abc123", %{app_metadata: %{admin: true}})
User.delete("auth0|abc123")
#=> :ok
```
## Finding a user by email
If you have an address and want the user, use `by_email/2` rather than a search:
```elixir
User.by_email("someone@example.com")
#=> {:ok, [%{"user_id" => "auth0|abc123", ...}]}
```
It is an exact, case-insensitive match, and returns a list because one address can belong to several
identities across connections. `User.all/1` would go through the search engine instead, with the caveats
below.
## Searching
`User.all/1` uses the v3 search engine, the only one Auth0 still supports. It differs from v2 in ways that
return nothing rather than erroring:
- `.raw` sub-fields are gone — query the field directly
- `connection` became `identities.connection`
- matching is case-sensitive
- **results cap at 1,000 records**
For anything larger, run an export rather than paginating.
## Password resets
To email the user a reset link, use `Auth0Client.Authentication.change_password/3` — see
[Logging users in](login.md). To generate a link yourself:
```elixir
Auth0Client.Management.Ticket.password_change(%{user_id: "auth0|abc123"})
#=> {:ok, %{"ticket" => "https://your-tenant.auth0.com/lo/reset?ticket=..."}}
```
## Sessions and tokens
From the user's side — everything at once:
```elixir
User.sessions("auth0|abc123")
User.delete_sessions("auth0|abc123") # signs them out everywhere
User.refresh_tokens("auth0|abc123")
User.delete_refresh_tokens("auth0|abc123")
User.revoke_access("auth0|abc123") # both at once
```
Or one at a time, using the ids those listings return:
```elixir
alias Auth0Client.Management.{RefreshToken, Session}
Session.get(session_id)
Session.delete(session_id) # ends the session
Session.revoke(session_id) # ends it AND its refresh tokens
RefreshToken.get(token_id)
RefreshToken.delete(token_id)
RefreshToken.revoke(%{ids: [token_id]}) # in bulk, up to 100
```
> #### `delete` and `revoke` differ where it matters {: .warning}
>
> `Session.delete/1` ends the session but leaves its refresh tokens alive, so an application holding one
> gets a new access token immediately. After a credential compromise, `Session.revoke/1` is the call that
> actually locks the user out.
`RefreshToken.revoke/1` takes an **exclusive choice**, not a set of filters: `ids`, or `user_id`, or
`user_id` + `client_id`, or those plus `audience`. `client_id` alone is rejected, and `ids` combines with
nothing.
`RefreshToken.all/1` is the same listing as `User.refresh_tokens/2` — `user_id` is required either way —
except that it also filters by `client_id`.
To revoke a refresh token as its *holder* rather than as an administrator, with no Management token
involved, use `Auth0Client.Authentication.Token.revoke/3` — see [Logging users in](login.md).
To sign a user out of **every application**, not just this tenant's,
`Auth0Client.Authentication.global_token_revocation/3` is Auth0's Universal Logout endpoint. It authenticates
with a bearer token you supply rather than a management token, and — like everything above — it destroys
sessions and refresh tokens but leaves already-issued access tokens working until they expire.
### Consent grants
A grant records that a user consented to an application's scopes. Deleting one makes Auth0 prompt for
consent again and invalidates the refresh tokens issued under it.
```elixir
alias Auth0Client.Management.Grant
Grant.all(user_id: "auth0|abc123")
Grant.delete("cgr_abc123") # one application
Grant.delete_by_user("auth0|abc123") # every application, all at once
```
`delete_by_user/1` takes the user id as a query parameter rather than a path segment, which is why it is a
separate function.
## Bulk import and export
Jobs are asynchronous: they return immediately and do the work in the background, so start one, poll it,
then check for errors.
```elixir
alias Auth0Client.Management.Job
# Export — poll until the job carries a `location` to download from
{:ok, job} = Job.users_exports(%{connection_id: "con_abc123", format: "json"})
{:ok, %{"status" => "completed", "location" => url}} = Job.get(job["id"])
# Import — takes the contents of a JSON file, or a stream for a large one
{:ok, job} = Job.users_imports(File.read!("users.json"), "con_abc123", %{upsert: true})
{:ok, job} = Job.users_imports(File.stream!("users.json"), "con_abc123")
```
Import is the one endpoint Auth0 accepts only as `multipart/form-data`; the library handles that for you,
including turning `upsert: true` into the string form Auth0 expects.
### Checking the result
`Job.errors/1` answers `:ok` when the import was clean and `{:ok, errors}` when some records failed —
Auth0 returns 204 in the first case. The success path is the surprising one, so match both:
```elixir
case Job.errors(job["id"]) do
:ok -> :imported_cleanly
{:ok, errors} -> handle(errors)
end
```