# Use service accounts

Create a service account for CI and scripts, exchange its client ID and secret for an access token, grant it roles, and revoke it.

A service account is a non-interactive credential for automation such as CI pipelines, scripts and infrastructure-as-code tools. It belongs to one organization and acts as its own principal, `service_account:<service-account-id>`. Its access is checked exactly like a user’s: it can do what its role assignments allow, and nothing else.

A service account authenticates with an OAuth 2.0 client ID and client secret, using the client credentials grant against the platform’s identity provider. The resulting access token is sent to the API like a user’s.

## Before you begin [#before-you-begin]

<BeforeYouBegin>

- To create or revoke a service account: an org-wide `roles/owner` assignment.
- To list service accounts: an org-wide `roles/owner` or `roles/viewer` assignment.
- Your organization ID (`<org-id>`).

</BeforeYouBegin>

## Create a service account [#create-a-service-account]

1. Create the service account with a name that tells you what uses it.

   ```sh
   origin iam create-service-account --org-id <org-id> --name ci-deploy
   ```

   The output contains the service account’s `id`, its `principal`, and the credential: `clientId` and `clientSecret`.

   <Callout type="warning" title="Important">

   The client secret is shown only once. Store it in your secret manager now. No API returns it again; if you lose it, create a new service account.

   </Callout>

1. Grant the service account the roles it needs. A new service account holds no roles and can’t do anything until you assign one. Use the `principal` from the previous step.

   ```sh
   origin iam assign-role --org-id <org-id> \
     --project-id <project-id> \
     --principal service_account:<service-account-id> \
     --role-urn urn:origin:cloud:iam::::tc/role/roles/viewer
   ```

   Grant the smallest role and scope that does the job. See [Assign and revoke roles](/docs/iam/assign-roles).

1. Verify that the service account is listed as active. An active service account has no `revokedAt` value.

   ```sh
   origin iam list-service-accounts --org-id <org-id>
   ```

## Get an access token [#get-an-access-token]

The `origin` CLI logs in people only. Automation that runs as a service account calls the GraphQL API directly.

1. Request an access token from the token endpoint with the client credentials grant.

   ```sh
   curl -s \
     https://auth.origincloud.eu/realms/origincloud/protocol/openid-connect/token \
     -d grant_type=client_credentials \
     -d client_id=<client-id> \
     -d client_secret=<client-secret>
   ```

   The response is JSON with an `access_token` and its lifetime in seconds, `expires_in`. Request a new token when it expires.

1. Send the token as a bearer token with each API request.

   ```sh
   curl -s https://api.origincloud.eu/graphql \
     -H "Authorization: Bearer <access-token>" \
     -H "Content-Type: application/json" \
     -d '{"query":"{ iam { permissions { key } } }"}'
   ```

1. Verify that the token is accepted: the response contains a `data` object rather than an authentication error.

## Revoke a service account [#revoke-a-service-account]

Revoking a service account disables its credential. From that moment, every request for a new access token is refused. An access token issued before the revocation stays valid until it expires.

1. Find the service account’s ID with `list-service-accounts`.
1. Revoke it.

   ```sh
   origin iam revoke-service-account --org-id <org-id> \
     --service-account-id <service-account-id>
   ```

1. Revoke its role assignments as well. Revoking the service account leaves them in place, and removing them cuts off any access token that is still valid, because roles are checked on every request.

   ```sh
   origin iam remove-member --org-id <org-id> \
     --principal service_account:<service-account-id>
   ```

1. Run `list-service-accounts` again and check that the service account has a `revokedAt` time. Revoked service accounts stay in the list. A revoked service account can’t be reactivated.

## Rotate a client secret [#rotate-a-client-secret]

A client secret can’t be changed or shown again. To rotate it, replace the service account with a new one:

1. Create a new service account, as in [Create a service account](#create-a-service-account), and store its client secret.
1. List the old service account’s role assignments, with their project scope:

   ```sh
   origin iam principal-grants --org-id <org-id> \
     --principal service_account:<old-service-account-id>
   ```

1. Assign the same roles, at the same scopes, to the new service account’s principal with `origin iam assign-role`.
1. Switch your automation to the new client ID and secret.
1. Revoke the old service account and remove its role assignments, as in [Revoke a service account](#revoke-a-service-account).

## Using the API [#using-the-api]

The mutations are `createServiceAccount` and `revokeServiceAccount`; the query is `serviceAccounts`, all in the `iam` namespace. `serviceAccounts` never returns a client secret.

```graphql
mutation {
  iam {
    createServiceAccount(input: { orgId: "<org-id>", name: "ci-deploy" }) {
      id
      principal
      clientId
      clientSecret
    }
  }
}
```

## Related [#related]

- [Access model](/docs/iam/access-model)
- [Built-in roles](/docs/iam/roles)
- [API access](/docs/platform/access/api)
- [`create-service-account`](/docs/iam/reference/cli#create-service-account) and [`revoke-service-account`](/docs/iam/reference/cli#revoke-service-account) commands
- [Audit log](/docs/iam/audit-log) – creation and revocation are recorded

