Keys That Never Die: We Audited 20 Credential Configurations Across 19 Major Platforms
A documentation audit of 20 credential configurations across 19 developer, cloud, SaaS, and data platforms finds that only 7 expire by default, 5 are documented to never expire, and 8 have no documented default expiration at all.
By Sai Vashisht · Identra Research ·
TL;DR
- Only 7 of the 20 credential configurations we reviewed expire by default: GitHub fine-grained PATs, GitLab, Atlassian, npm, Microsoft Azure client secrets, Databricks, and Snowflake.
- 5 configurations are explicitly documented as never expiring unless someone rotates or revokes them (AWS IAM access keys, Google Cloud service account keys, Salesforce refresh tokens, Slack OAuth tokens, HubSpot private app tokens). For 8 more, including OpenAI API keys and GitHub classic PATs, the official documentation states no default expiration at all, which is its own finding.
- Only 9 of the 20 configurations, on 9 of the 19 platforms, come with a documented administrator control that can enforce a maximum credential lifetime. For 10 configurations no publicly documented control was found, and GitHub documents that classic PATs have no expiration requirement.
Of 20 credential configurations across 19 major platforms, only 7 expire by default. 5 are documented to never expire, and for 8 the documentation states no default expiration at all.
The forever key is still the norm, not the exception
Every breach retrospective seems to feature the same character: a token or key created years ago, forgotten, and still valid. We wanted to know how much of that is user negligence and how much is platform design. So we read the official documentation of 19 widely used platforms across four categories (developer tools, cloud providers, SaaS integrations, and data/AI services) and recorded, for 20 credential configurations, three things: does the credential expire by default, what does the documentation say about non-expiring credentials, and can an administrator enforce expiry. GitHub contributes two configurations, because its fine-grained and classic personal access tokens are documented separately and behave differently.
The results are lopsided. Only 7 of the 20 configurations put an expiration on a new credential without being asked. 5 are explicitly documented as never expiring unless the user opts in to rotation or revokes them. And for the remaining 8, the official documentation states no default expiration at all: it either describes an expiration chooser without naming a default or says nothing about expiry in either direction. We treat that third state as its own finding, distinct from never expires, because a claim the docs do not make is not a claim we can verify.
Five configurations are documented to never expire
On five configurations, the vendor's own documentation states that the credential does not expire unless someone acts: AWS (IAM user access keys), Google Cloud (user-managed service account keys), Salesforce (connected app OAuth refresh tokens under the default policy), Slack (bot and user OAuth tokens), and HubSpot (private app access tokens).
The documentation language is explicit. AWS describes long-term access keys that never expire and offers monitoring, rotation, and deletion as the remedies. Google Cloud states that by default, service account keys don't have an expiry time and stay valid until you delete them. Slack's docs say that without token rotation, the access token never expires. Salesforce's default refresh token policy is valid until revoked, meaning the refresh token is used indefinitely unless a user or admin revokes it. HubSpot's official developer content states that private app tokens don't expire; the platform emails super admins a rotation reminder after 180 days, but nothing actually expires.
Some of these vendors clearly know this is a problem. Google Cloud offers an org-level policy that puts an expiry on newly created keys. Salesforce lets admins tighten the refresh token policy per connected app. But the default path, which is likely the one many users take, still ends in a forever key.
The documentation gap: eight configurations with no stated default
Eight configurations landed in a third state that surprised us more than the forever keys did: the official documentation states no default expiration. This is not the same claim as never expires. It means the docs either describe an expiration field without naming a default or say nothing about expiry in either direction, so neither we nor a security team doing a review can answer the most basic lifecycle question from the vendor's own material.
The reasons vary. Zoom's server-to-server access tokens expire every hour without exception, but the client secret used to mint them has no documented lifetime at all. Anthropic makes expiration a required choice at creation, with presets from 3 hours to 30 days, a custom duration, and Never, but documents no preselected default. GitHub's docs recommend adding an expiration to classic PATs, which implies non-expiring classic tokens are possible, without stating a default.
- GitHub (classic PAT): no default expiration documented; docs recommend adding one.
- Docker Hub: the creation flow documents an expiration date field but no default and no duration limits.
- DigitalOcean: docs describe choosing when the token expires but state no default.
- Notion: the authorization docs say nothing about integration secret expiry in either direction.
- Zoom: hourly access tokens on top of a client secret with no documented lifetime.
- OpenAI: no expiration mechanism is documented in the official material we could access.
- Anthropic: expiration is a required choice at creation, including a Never option, with no documented default.
- MongoDB Atlas: docs call API keys a legacy method and document no expiry for the key pair in either direction.
Who actually forces an expiry date
Seven of the 20 configurations expire by default, and the group skews heavily toward credentials designed or redesigned recently. GitHub fine-grained PATs default to 30 days if no expiration is provided. npm's granular tokens, after the 2025 supply-chain incidents, default to 7 days for write access and cap at 90. Snowflake programmatic access tokens default to 15 days. GitLab PATs default to 365 days and removed non-expiring tokens entirely in version 16.0. Atlassian API tokens created after December 15, 2024 expire in one year by default, with a creation range of 1 day to 1 year; an authentication policy can set expiration from 1 to 365 days or Never, so the cap is not strictly hard. Azure client secrets must have an expiration, capped at 24 months. Databricks PATs default to the workspace maximum, typically 730 days.
The pattern is hard to miss: when a vendor ships a new credential type or revisits an old one after an incident, it expires by default. The forever keys are almost all legacy designs that predate the current threat environment and have never been forced to change.
The admin control gap
The default matters most when an administrator cannot override it. Only 9 of the 20 configurations, on 9 of the 19 platforms, come with a documented org-level control that can enforce a maximum credential lifetime: GitHub (fine-grained PATs only), GitLab, Atlassian (an authentication policy setting; more than one policy requires Atlassian Guard Standard), Google Cloud, Microsoft Azure, Salesforce, Anthropic (an organization maximum expiration policy that removes the Never option), Databricks, and Snowflake.
GitHub is the one platform where the docs answer the question in the negative: the org PAT policy page states that personal access tokens (classic) do not have an expiration requirement, so organizations can block classic PATs entirely but cannot enforce a lifetime on them. For the other 10 configurations (npm, Docker Hub, AWS, DigitalOcean, Slack, Notion, HubSpot, Zoom, OpenAI, and MongoDB Atlas) no publicly documented control was found. On those platforms your credential hygiene is exactly as good as the discipline of every individual who has ever clicked Create Token, and no better. AWS deserves special mention: the access key documentation describes no expiry setting at all, so the largest cloud provider documents monitoring and manual revocation as the only remedies for a 6-year-old access key.
| Admin can enforce a lifetime | Configurations | Count |
|---|---|---|
| Yes, documented | GitHub (fine-grained PAT), GitLab, Atlassian (requires Atlassian Guard Standard for more than one policy), Google Cloud, Microsoft Azure, Salesforce, Anthropic, Databricks, Snowflake | 9 |
| No, documented as not enforceable | GitHub (classic PAT) | 1 |
| No publicly documented control found | npm, Docker Hub, AWS, DigitalOcean, Slack, Notion, HubSpot, Zoom, OpenAI, MongoDB Atlas | 10 |
What to do with this
If you run security for an organization that touches even half these platforms, three moves follow directly from the data. First, turn on the enforcement levers that exist: GitHub org policies for fine-grained PAT lifetimes, Google Cloud's service account key expiry constraint, Azure app management policies, Salesforce refresh token policies, Databricks token lifetime settings, Snowflake PAT policies, Atlassian Guard authentication policies, and Anthropic's organization maximum expiration policy are all documented, supported, and off by default. Second, on the platforms where no publicly documented enforcement lever was found, treat credential inventory as your control: you cannot make the platform expire tokens, so you have to know every token that exists, who created it, and when it was last used. Third, prefer the newer credential type wherever a vendor offers one, because the newer type almost always expires and the legacy type almost never does.
There is also a question worth putting to vendors directly: if npm can move write tokens to a 7-day default after an incident, and GitLab can delete the concept of a non-expiring token outright, what is stopping the platforms that still hand out forever keys by default?
Our view at Identra is that this is an identity problem wearing a credential costume. A key that never expires is an identity that never has to re-justify its existence: nobody re-approves it, nobody re-attests to needing it, and over time nobody remembers who is accountable for it. Expiry is not primarily a cryptographic control. It is a forcing function that makes a human periodically confirm that this access should still exist. That is why we build identity-first, and why we think the default lifetime of a credential is one of the most consequential security decisions a platform makes on its users' behalf.
The full dataset: 20 configurations across 19 platforms
| Platform | Credential | Default expiry | Admin can enforce lifetime | Source |
|---|---|---|---|---|
| GitHub | Fine-grained personal access token | Expires by default (30 days if none provided) | Yes (org maximum lifetime policy) | docs.github.com |
| GitHub | Personal access token (classic) | No expiration documented as default; docs recommend adding one | No (classic tokens have no expiration requirement) | docs.github.com |
| GitLab | Personal access token | Expires by default (365 days) | Yes (maximum lifetime, GitLab Ultimate) | docs.gitlab.com |
| Atlassian | Account API token | Expires by default (1 year since December 15, 2024) | Yes (authentication policy, 1 to 365 days or Never) | support.atlassian.com |
| npm | Granular access token | Expires by default (7 days for write tokens, 90-day cap) | Not documented (the cap is platform-enforced) | github.blog |
| Docker Hub | Personal access token | No expiration documented (field exists, no default stated) | Not documented | docs.docker.com |
| AWS | IAM user access key | Never expires (documented long-term credential) | Not documented (no expiry setting in IAM docs) | docs.aws.amazon.com |
| Google Cloud | User-managed service account key | Never expires by default (valid until deleted) | Yes (org policy sets expiry for new keys) | cloud.google.com |
| Microsoft Azure | Entra app registration client secret | Expires by default (expiration mandatory, 24-month cap) | Yes (app management policies) | learn.microsoft.com |
| DigitalOcean | Personal access token | No expiration documented (docs state no default) | Not documented | docs.digitalocean.com |
| Salesforce | Connected app OAuth refresh token | Never expires by default (valid until revoked) | Yes (per-app refresh token policy) | help.salesforce.com |
| Slack | Bot/user OAuth token | Never expires (12 hours only with opt-in rotation) | Not documented | docs.slack.dev |
| Notion | Internal integration secret | No expiration documented (docs silent in either direction) | Not documented | developers.notion.com |
| HubSpot | Private app access token | Never expires (rotation is manual; 180-day reminder email) | Not documented | developers.hubspot.com |
| Zoom | Server-to-server OAuth client secret | No expiration documented for the secret (access tokens last 1 hour) | Not documented | developers.zoom.us |
| OpenAI | API key | No expiration documented in accessible official material | Not documented | platform.openai.com |
| Anthropic | Console API key | No default documented (expiration is a required choice: presets, custom, or Never) | Yes (org maximum expiration policy removes Never) | platform.claude.com |
| Databricks | Personal access token | Expires by default (workspace maximum, typically 730 days) | Yes (admins set maximum lifetime) | docs.databricks.com |
| Snowflake | Programmatic access token | Expires by default (15 days) | Yes (authentication policy PAT_POLICY) | docs.snowflake.com |
| MongoDB Atlas | Programmatic API key | No expiration documented for the key pair | Not documented | mongodb.com |
All rows checked against vendor documentation on July 12, 2026.
Methodology
This is a documentation audit, not a telemetry study. We reviewed the official documentation of 19 platforms across four categories: developer tools (GitHub, GitLab, Atlassian, npm, Docker Hub), cloud providers (AWS, Google Cloud, Microsoft Azure, DigitalOcean), SaaS platforms (Salesforce, Slack, Notion, HubSpot, Zoom), and data/AI services (OpenAI, Anthropic, Databricks, Snowflake, MongoDB Atlas). GitHub's fine-grained and classic personal access tokens are documented separately and behave differently, so the dataset covers 20 credential configurations across 19 platforms, and both denominators are used above. Default expiry is recorded in one of three states: expires by default (the docs state a new credential gets an expiration without user action), never expires by default (the docs explicitly state the credential does not expire or stays valid until revoked), or no expiration documented (the docs are silent on default expiry, or document an expiration chooser without a stated default). We do not infer never expires from documentation silence. Admin enforceability is recorded on the same standard: yes, no, or not documented. Sources were limited to official vendor documentation, official changelogs, and official vendor developer content; no third-party reporting was used. All rows were checked against vendor documentation on July 12, 2026. Limitations: documentation can lag product behavior; two sources (Salesforce help and OpenAI platform docs) were not directly fetchable during the final check and are classified conservatively; enterprise tiers may offer controls not covered in the public docs we cited; and this audit describes what platforms are documented to do, not what their users actually configure. Every row in the dataset table links to the specific documentation page it was drawn from.
