# Auth API

The Auth API is the OAuth flow that authenticates a *ServiceUser* (an end-user of the product a *Space* runs) through social login. When the user signs in with a provider connected to the *ServiceLogin* configuration (for example, Google), this API issues an `accessToken` and a `refreshToken`. The issued `accessToken` is a Bearer token used only for ACMA and ACDA calls; it cannot be used with CMA or CDA. No token crosses the identity boundary.

The base URL is `https://auth.weegloo.com/v1`, and every path lives under `/spaces/{spaceId}/...`. All request and response bodies are JSON. For browser apps, we recommend using the official SDK [`weegloo-service-user`](https://www.npmjs.com/package/weegloo-service-user) rather than handling these HTTP requests and responses directly. This page covers the HTTP request and response formats that the SDK calls under the hood. Refer to it when you implement the flow yourself in an environment where the SDK is unavailable (server, native, scripts).

## Login flow {#login-flow}

The login flow below assumes a web browser app. Native apps (Android and iOS) handle the callback differently and are covered separately in [Native app callback](#native-app-callback).

Login happens in four steps.

1. Navigate the browser to the login entry URL (`/spaces/{spaceId}/login/oauth2/{provider}`). This URL starts a redirect chain that leads to the provider (Google) login screen.
2. Once login finishes, Weegloo sends the browser back to the `callbackUrl` configured in *ServiceLogin*, appending `?exchangeToken=<one-time token>` to the address.
3. The callback page reads the `exchangeToken` from the address and sends it to the token exchange endpoint (`POST /spaces/{spaceId}/oauth/token`), receiving an `accessToken` and a `refreshToken` in response.
4. From then on, call ACMA and ACDA with the `accessToken` as a Bearer token. Renew it with the `refreshToken` before it expires (`expiresAt`), and revoke the tokens when you log out.

The `exchangeToken` is one-time use. Immediately after handling the callback, remove it from the address bar at once to prevent exposure and reuse (the SDK handles this automatically).

## Native app callback: the deep link bridge {#native-app-callback}

Android and iOS native apps sign in through the same OAuth flow. However, `callbackUrl` accepts only a web address, so the way they connect is different.

As documented, the `callbackUrl` is a web address such as `https://...`. A custom scheme registered by the app, like `myapp://`, is rejected, so it cannot be used directly as the callback address. For that reason, a native app sets its `callbackUrl` to an `https` bridge page hosted on *Web Hosting*, and that page forwards the `?exchangeToken=...` carried in the callback to the app's deep link (for example, `myapp://auth/callback?exchangeToken=...`). The app then performs the token exchange (step 3 of the login flow above, `POST /spaces/{spaceId}/oauth/token`) itself with that `exchangeToken`.

- Only the one-time `exchangeToken` is passed through the deep link. The `accessToken` and `refreshToken` do not travel over the deep link, because the app exchanges for them itself. The bridge page also removes the `exchangeToken` from the address after passing the value along (the same principle as the callback in the login flow).
- Use verified deep links (Android App Links, iOS Universal Links) when possible. An unverified custom scheme can be intercepted by another app installed on the device.
- Create the provider's (for example, Google) OAuth client as the `Web application` type even for a native app. This is because the OAuth redirect goes to `auth.weegloo.com` (a web address), not to the app.

How to deploy the bridge page is covered in [Web Hosting](/getting-started/core-concepts/deployment-and-integration/web-hosting.md).

## Token model {#token-model}

Token exchange and renewal return a token response of the same shape. The token strings and timestamps carried in the response are the example values below; in reality they are opaque secret strings (because the flow goes through a provider login, the actual values cannot be reproduced here).

```json
{
  "accessToken": "QY3xK9pR2mLs7Vc0Zt8Nf4Wd1Bj6Hg5Ua2Ee9Ck3PoZt8Nf4Wd",
  "tokenType": "Bearer",
  "scope": ["APP"],
  "createdAt": "2026-06-18T05:00:00.000Z",
  "expiresAt": "2026-06-19T05:00:00.000Z",
  "refreshToken": "Rf7Hn2Qw9Zx4Tp1Lk6Vc3Bm8Yd5Gs0Ae2Uj7Co4NeLk6Vc3Bm",
  "refreshExpiresAt": "2026-06-21T05:00:00.000Z"
}
```

| Field | Type | Description |
|---|---|---|
| `accessToken` | string | The Bearer token used for ACMA and ACDA calls. |
| `tokenType` | string | The token kind. Always `"Bearer"`. |
| `scope` | string array | The token's permission scope. A *ServiceUser* token is `["APP"]`. |
| `createdAt` | string (date-time) | The time the token was issued. |
| `expiresAt` | string (date-time) | The expiration time of the `accessToken`. |
| `refreshToken` | string | The token used to renew the `accessToken`. |
| `refreshExpiresAt` | string (date-time) | The expiration time of the `refreshToken`. Three days after `createdAt`. |

The three tokens have the following lifetimes.

- The `exchangeToken` is one-time use and short-lived. It must be exchanged right after the callback. It is not included in the exchange response; it is delivered through the address in step 2 of the login flow.
- The lifetime of the `accessToken` is not a fixed value. The exact expiration time is carried in the response's `expiresAt`, so judge by that value. It is for ACMA and ACDA only.
- The `refreshToken` is valid for three days after issuance (`refreshExpiresAt`). Calling renewal issues a new `accessToken` and `refreshToken` pair and revokes the previous pair (rotation). Each time you renew, the previous `refreshToken` can no longer be used.

## API {#api}

The base URL for all four endpoints below is `https://auth.weegloo.com/v1`. They are covered in order: login entry (GET), token exchange (POST), token renewal (POST), and logout (DELETE).

```api-endpoint
{
  "title": "Login entry",
  "method": "GET",
  "path": "/spaces/{spaceId}/login/oauth2/{provider}",
  "description": "The entry URL that starts the social login flow. It is a target for browser navigation, not for fetch: assign it to window.location so the browser follows the OAuth redirect chain. The response is a 302 redirect toward the provider login, with no JSON body.",
  "responseStatus": 302,
  "baseUrl": "https://auth.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "provider": { "type": "string", "description": "The social login provider identifier. Example: google", "required": true }
  },
  "requestHeaderSchema": {}
}
```

```api-endpoint
{
  "title": "Token exchange",
  "method": "POST",
  "path": "/spaces/{spaceId}/oauth/token",
  "description": "Exchanges the exchangeToken received from the callback for an accessToken and a refreshToken. Send the exchangeToken in the request body. On success it returns the token response with 200.",
  "responseStatus": 200,
  "baseUrl": "https://auth.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "requestHeaderSchema": {},
  "requestBodySchema": {
    "type": "object",
    "required": ["exchangeToken"],
    "properties": {
      "exchangeToken": {
        "type": "string",
        "minLength": 1,
        "maxLength": 256,
        "description": "The ?exchangeToken= value from the callback address (one-time use)"
      }
    },
    "example": {
      "exchangeToken": "Ex9Tk2Lm5Qz8Rn1Vb4Wc7Hd0Js3Pf6Ua9Yg2Ke5"
    }
  },
  "responseExample": {
    "accessToken": "QY3xK9pR2mLs7Vc0Zt8Nf4Wd1Bj6Hg5Ua2Ee9Ck3PoZt8Nf4Wd",
    "tokenType": "Bearer",
    "scope": ["APP"],
    "createdAt": "2026-06-18T05:00:00.000Z",
    "expiresAt": "2026-06-19T05:00:00.000Z",
    "refreshToken": "Rf7Hn2Qw9Zx4Tp1Lk6Vc3Bm8Yd5Gs0Ae2Uj7Co4NeLk6Vc3Bm",
    "refreshExpiresAt": "2026-06-21T05:00:00.000Z"
  }
}
```

```api-endpoint
{
  "title": "Token renewal",
  "method": "POST",
  "path": "/spaces/{spaceId}/oauth/refresh",
  "description": "Issues a new accessToken and refreshToken pair using the refreshToken. On success it returns the token response with 200. The previous pair is revoked at the same time as renewal (rotation).",
  "responseStatus": 200,
  "baseUrl": "https://auth.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "requestHeaderSchema": {},
  "requestBodySchema": {
    "type": "object",
    "required": ["refreshToken"],
    "properties": {
      "refreshToken": {
        "type": "string",
        "minLength": 1,
        "maxLength": 256,
        "description": "The refreshToken issued previously"
      }
    },
    "example": {
      "refreshToken": "Rf7Hn2Qw9Zx4Tp1Lk6Vc3Bm8Yd5Gs0Ae2Uj7Co4NeLk6Vc3Bm"
    }
  },
  "responseExample": {
    "accessToken": "Nw5Bt8Kc1Xr4Lp7Zd0Qf3Vm6Hg9Sa2Ej5Uy8MoQf3Vm6Hg9Sa",
    "tokenType": "Bearer",
    "scope": ["APP"],
    "createdAt": "2026-06-18T11:00:00.000Z",
    "expiresAt": "2026-06-19T11:00:00.000Z",
    "refreshToken": "Tg2Md6Pn9Yx3Qw7Lk0Vc4Bm8Hd1Js5Ae9Uo2CeLk0Vc4Bm8H",
    "refreshExpiresAt": "2026-06-21T11:00:00.000Z"
  }
}
```

```api-endpoint
{
  "title": "Logout",
  "method": "DELETE",
  "path": "/spaces/{spaceId}/oauth/token",
  "description": "Logs out with the current accessToken. A Bearer token is required in the Authorization header. If you send a refreshToken in the body, that refreshToken is revoked as well. The body is optional; if you do not send it, the refreshToken stays valid until it expires on its own. On success it responds with 204 No Content and no response body.",
  "responseStatus": 204,
  "baseUrl": "https://auth.weegloo.com/v1",
  "authIdentity": "service-user",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (accessToken)", "required": true }
  },
  "requestBodySchema": {
    "type": "object",
    "properties": {
      "refreshToken": {
        "type": "string",
        "minLength": 1,
        "maxLength": 256,
        "description": "The refreshToken to revoke as well (optional)"
      }
    },
    "example": {
      "refreshToken": "Rf7Hn2Qw9Zx4Tp1Lk6Vc3Bm8Yd5Gs0Ae2Uj7Co4NeLk6Vc3Bm"
    }
  }
}
```

## Related documents {#related-documents}

- [ServiceUser login (concept)](/getting-started/core-concepts/service-users/service-login.md): How to configure *ServiceLogin* in the content studio.
- [ACMA](/api/reference/acma.md): The API for working with member content using the issued token.
- [ACDA](/api/reference/acda.md): The read API for delivery to members.
