Mastra keeps its own auth hook. Authdog stays the identity provider. The sample attaches an agent access token there and refuses the run when that token is missing or userinfo rejects it.

Sample: [mastra/typescript](https://github.com/authdog/agents-samples/tree/main/mastra/typescript). Node.js. Package `@mastra/core@1.79.0`.

Channel auth, machine credentials, tool checks, and instructions for this framework are the other pages under Mastra in [AI](/docs/ai/mastra/channel-auth).

## The hook

```typescript
export const mastra = new Mastra({
  server: {
    auth: {
      authenticateToken: async (token) => {
        const subject = await subjectIfAccepted(token)
        if (!subject) return null
        return { id: subject }
      },
    },
  },
})
```

Mastra routes stay public when `server.auth` is unset. With `authenticateToken` set, a route that does not opt out requires a user. Returning no user is unauthorized, and the response is `401`. `whoami` returns `id`. A public route would have to set `requiresAuth: false`. This sample does not. Use the result the installed `@mastra/core` already treats as "no user" (`null`, or a rejected promise if that version requires it).

This is agent identity: `client_credentials`, then this hook. It is not [Agent Sign-In](/docs/agent-sign-in). A browser sign-in does not replace the hook.

## What you should see

A request with no bearer is `401`. A bearer userinfo rejects is `401`. The body has no subject.

A bearer userinfo accepts reaches `whoami`, which returns that subject.

`npm test` in `mastra/typescript` checks both. It does not call a model.

## Configure

| Name | Purpose |
| --- | --- |
| `PK_AUTHDOG` | Public key on the Mastra server. Selects the identity host. |
| `Authorization` | Access token the caller presents. |

The shared exchange is in [Agent frameworks](/docs/agent-frameworks#mint-the-bearer). There is no refresh token. Repeat it before `expires_in`.

> **Caution**
>
> Do not commit the client secret, and do not put the access token in the prompt. The public key selects the identity host. It does not mint tokens.

## Related

| Read | To learn how to |
| --- | --- |
| [Agent frameworks](/docs/agent-frameworks) | Mint the bearer this hook checks |
| [Claude Agent SDK](/docs/agent-frameworks/claude-agent) | The previous hook |
| [LlamaIndex Workflows](/docs/agent-frameworks/llamaindex) | The next hook |
| [Custom channel auth for Mastra](/docs/ai/mastra/channel-auth) | The same check, as the auth boundary |
