- Personal access tokens
- OAuth2 tokens
- Impersonation tokens
- Project access tokens
- Group access tokens
- Deploy tokens
- Deploy keys
- Runner authentication tokens
- Runner registration tokens (deprecated)
- CI/CD job tokens
- GitLab cluster agent tokens
- Other tokens
- Available scopes
- Security considerations
- Expired access tokens
- Troubleshooting
GitLab Token overview
This document lists tokens used in GitLab, their purpose and, where applicable, security guidance.
Personal access tokens
You can create Personal access tokens to authenticate with:
- The GitLab API.
- GitLab repositories.
- The GitLab registry.
You can limit the scope and expiration date of your personal access tokens. By default, they inherit permissions from the user who created them.
You can use the personal access tokens API to programmatically take action, such as rotating a personal access token.
You will receive an email when personal access tokens are 7 days or less from expiration.
OAuth2 tokens
GitLab can serve as an OAuth2 provider to allow other services to access the GitLab API on a user’s behalf.
You can limit the scope and lifetime of your OAuth2 tokens.
Impersonation tokens
An Impersonation token is a special type of personal access token. It can be created only by an administrator for a specific user. Impersonation tokens can help you build applications or scripts that authenticate with the GitLab API, repositories, and the GitLab registry as a specific user.
You can limit the scope and set an expiration date for an impersonation token.
Project access tokens
Project access tokens are scoped to a project. As with Personal access tokens, you can use them to authenticate with:
- The GitLab API.
- GitLab repositories.
- The GitLab registry.
You can limit the scope and expiration date of project access tokens. When you create a project access token, GitLab creates a bot user for projects. Bot users for projects are service accounts and do not count as licensed seats.
You can use the project access tokens API to programmatically take action, such as rotating a project access token.
All project maintainers receive an email when project access tokens are 7 days or less from expiration.
Group access tokens
Group access tokens are scoped to a group. As with Personal access tokens, you can use them to authenticate with:
- The GitLab API.
- GitLab repositories.
- The GitLab registry.
You can limit the scope and expiration date of group access tokens. When you create a group access token, GitLab creates a bot user for groups. Bot users for groups are service accounts and do not count as licensed seats.
You can use the group access tokens API to programmatically take action, such as rotating a group access token.
All group owners receive an email when group access tokens are 7 days or less from expiration.
Deploy tokens
Deploy tokens allow you to download (git clone
) or push and pull packages and container registry images of a project without having a user and a password. Deploy tokens cannot be used with the GitLab API.
Deploy tokens can be managed by project maintainers and owners.
Deploy keys
Deploy keys allow read-only or read-write access to your repositories by importing an SSH public key into your GitLab instance. Deploy keys cannot be used with the GitLab API or the registry.
This is useful, for example, for cloning repositories to your Continuous Integration (CI) server. By using deploy keys, you don’t have to set up a fake user account.
Project maintainers and owners can add or enable a deploy key for a project repository
Runner authentication tokens
In GitLab 16.0 and later, to register a runner, you can use a runner authentication token instead of a runner registration token. Runner registration tokens have been deprecated.
After you create a runner and its configuration, you receive a runner authentication token
that you use to register the runner. The runner authentication token is stored locally in the
config.toml
file, which
you use to configure the runner.
The runner uses the runner authentication token to authenticate with GitLab when it picks up jobs from the job queue. After the runner authenticates with GitLab, the runner receives a job token, which it uses to execute the job.
The runner authentication token stays on the runner machine. The execution environments for the following executors only have access to the job token and not the runner authentication token:
- Docker Machine
- Kubernetes
- VirtualBox
- Parallels
- SSH
Malicious access to a runner’s file system may expose the config.toml
file and the
runner authentication token. The attacker could use the runner authentication token
to clone the runner.
You can use the runners
API to
programmatically rotate or revoke a runner authentication token.
Runner registration tokens (deprecated)
Runner registration tokens are used to register a runner with GitLab. Group or project owners or instance administrators can obtain them through the GitLab user interface. The registration token is limited to runner registration and has no further scope.
You can use the runner registration token to add runners that execute jobs in a project or group. The runner has access to the project’s code, so be careful when assigning project and group-level permissions.
CI/CD job tokens
The CI/CD job token is a short lived token only valid for the duration of a job. It gives a CI/CD job access to a limited amount of API endpoints. API authentication uses the job token, by using the authorization of the user triggering the job.
The job token is secured by its short life-time and limited scope. It could possibly be leaked if multiple jobs run on the same machine (like with the shell runner). On Docker Machine runners, configuring MaxBuilds=1
is recommended to make sure runner machines only ever run one build and are destroyed afterwards. This may impact performance, as provisioning machines takes some time.
GitLab cluster agent tokens
When registering a GitLab Agent for Kubernetes, GitLab generates an access token to authenticate the cluster agent with GitLab.
To revoke this cluster agent token, you can use either the:
- Agents API to revoke the token.
- UI to reset the token.
For both methods, you must know the token, agent, and project IDs. To find this information, use the Rails console
# Find token ID
Clusters::AgentToken.find_by_token('glagent-xxx').id
# Find agent ID
Clusters::AgentToken.find_by_token('glagent-xxx').agent.id
=> 1234
# Find project ID
Clusters::AgentToken.find_by_token('glagent-xxx').agent.project_id
=> 12345
You can also revoke a token directly in the Rails console:
# Revoke token with RevokeService, including generating an audit event
Clusters::AgentTokens::RevokeService.new(token: Clusters::AgentToken.find_by_token('glagent-xxx'), current_user: User.find_by_username('admin-user')).execute
# Revoke token manually, which does not generate an audit event
Clusters::AgentToken.find_by_token('glagent-xxx').revoke!
Other tokens
Feed token
Each user has a long-lived feed token that does not expire. This token allows authentication for:
- RSS readers to load a personalized RSS feed.
- Calendar applications to load a personalized calendar.
You cannot use this token to access any other data.
The user-scoped feed token can be used for all feeds, however feed and calendar URLs are generated with a different token that is only valid for one feed.
Anyone who has your token can read activity and issue RSS feeds or your calendar feed as if they were you, including confidential issues. If that happens, reset the token.
Incoming email token
Each user has a long-lived incoming email token that does not expire. This token allows a user to create a new issue by email, and is included in that user’s personal project-specific email addresses. You cannot use this token to access any other data. Anyone who has your token can create issues and merge requests as if they were you. If that happens, reset the token.
Available scopes
This table shows available scopes per token. Scopes can be limited further on token creation.
API access | Registry access | Repository access | |
---|---|---|---|
Personal access token | ✅ | ✅ | ✅ |
OAuth2 token | ✅ | 🚫 | ✅ |
Impersonation token | ✅ | ✅ | ✅ |
Project access token | ✅(1) | ✅(1) | ✅(1) |
Group access token | ✅(2) | ✅(2) | ✅(2) |
Deploy token | 🚫 | ✅ | ✅ |
Deploy key | 🚫 | 🚫 | ✅ |
Runner registration token | 🚫 | 🚫 | ✴️(3) |
Runner authentication token | 🚫 | 🚫 | ✴️(3) |
Job token | ✴️(4) | 🚫 | ✅ |
- Limited to the one project.
- Limited to the one group.
- Runner registration and authentication token don’t provide direct access to repositories, but can be used to register and authenticate a new runner that may execute jobs which do have access to the repository
- Limited to certain endpoints.
Security considerations
- Treat access tokens like passwords and keep them secure.
- When creating a scoped token, consider using the most limited scope possible to reduce the impact of accidentally leaking the token.
- When creating a token, consider setting a token that expires when your task is complete. For example, if performing a one-off import, set the token to expire after a few hours or a day. This reduces the impact of a token that is accidentally leaked because it is useless when it expires.
- If you have set up a demo environment to showcase a project you have been working on and you are recording a video or writing a blog post describing that project, make sure you are not leaking sensitive secrets (for example a personal access token (PAT), feed token or trigger token) during that process. If you have finished the demo, you must revoke all the secrets created during that demo. For more information, see revoking a PAT.
- Adding access tokens to URLs is a security risk, especially when cloning or adding a remote because Git then writes the URL to its
.git/config
file in plain text. URLs are also generally logged by proxies and application servers, which makes those credentials visible to system administrators. Instead, pass API calls an access token using headers like thePrivate-Token
header. - You can also store token using a Git credential storage.
- Do not:
- Store tokens in plain text in your projects.
- Include tokens when pasting code, console commands, or log outputs into an issue, MR description, or comment. Consider an approach such as using external secrets in CI.
- Do not log credentials in the console logs or artifacts. Consider protecting and masking your credentials.
- Review all active access tokens of all types on a regular basis and revoke any that are no longer needed. This includes:
- Personal, project, and group access tokens.
- Feed tokens.
- Trigger tokens.
- Runner registration tokens.
- Any other sensitive secrets etc.
Expired access tokens
If an existing access token is in use and reaches the expires_at
value, the token
expires and:
- Can no longer be used for authentication.
- Is not visible in the UI.
Requests made using this token return a 401 Unauthorized
response. Too many
unauthorized requests in a short period of time from the same IP address
result in 403 Forbidden
responses from GitLab.com.
For more information on authentication request limits, see Git and container registry failed authentication ban.
Identify expired access tokens from logs
Introduced in GitLab 17.2.
Prerequisites:
You must:
- Be an administrator.
- Have access to the
api_json.log
file.
To identify which 401 Unauthorized
requests are failing due to
expired access tokens, use the following fields in the api_json.log
file:
Field name | Description |
---|---|
meta.auth_fail_reason | The reason the request was rejected. Possible values: token_expired , token_revoked , insufficient_scope , and impersonation_disabled . |
meta.auth_fail_token_id | A string describing the type and ID of the attempted token. |
When a user attempts to use an expired token, the meta.auth_fail_reason
is token_expired
. The following shows an excerpt from a log
entry:
{
"status": 401,
"method": "GET",
"path": "/api/v4/user",
...
"meta.auth_fail_reason": "token_expired",
"meta.auth_fail_token_id": "PersonalAccessToken/12",
}
meta.auth_fail_token_id
indicates that an access token of ID 12 was used.
To find more information about this token, use the personal access token API. You can also use the API to rotate the token.
Replace expired access tokens
To replace the token:
- Check where this token may have been used previously, and remove it from any
automation might still use the token.
- For personal access tokens, use the API
to list tokens that have expired recently. For example, go to
https://gitlab.com/api/v4/personal_access_tokens
, and locate tokens with a specificexpires_at
date. - For project access tokens, use the project access tokens API to list recently expired tokens.
- For group access tokens, use the group access tokens API to list recently expired tokens.
- For personal access tokens, use the API
to list tokens that have expired recently. For example, go to
- Create a new access token:
- For personal access tokens, use the UI or Users API.
- For a project access token, use the UI or project access tokens API.
- For a group access token, use the UI or group access tokens API.
- Replace the old access token with the new access token. This process varies
depending on how you use the token, for example if configured as a secret or
embedded within an application. Requests made from this token should no longer
return
401
responses.
Troubleshooting
Identify personal, project, and group access tokens expiring on a certain date
Access tokens that have no expiration date are valid indefinitely, which is a security risk if the access token is divulged.
To manage this risk, when you upgrade to GitLab 16.0 and later, any personal, project, or group access token that does not have an expiration date automatically has an expiration date set at one year from the date of upgrade.
If you are not aware of when your tokens expire because the dates have changed, you might have unexpected authentication failures when trying to sign into GitLab on that date.
To manage this issue, you can use this tool that assists with analyzing, extending, or remove token expiration dates.
If you cannot run the tool, you can also run scripts in self-managed instances to identify tokens that either:
- Expire on a specific date.
- Have no expiration date.
You run these scripts from your terminal window in either:
- A Rails console session.
- Using the Rails Runner.
The specific scripts you run differ depending on if you have upgraded to GitLab 16.0 and later, or not:
- If you have not yet upgraded to GitLab 16.0 or later, identify tokens that do not have an expiration date.
- If you have upgraded to GitLab 16.0 or later, use scripts to identify any of the following:
After you have identified tokens affected by this issue, you can run a final script to extend the lifetime of specific tokens if needed.
These scripts return results in the following format:
Expired Group Access Token in Group ID 25, Token ID: 8, Name: Example Token, Scopes: ["read_api", "create_runner"], Last used:
Expired Project Access Token in Project ID 2, Token ID: 9, Name: Test Token, Scopes: ["api", "read_registry", "write_registry"], Last used: 2022-02-11 13:22:14 UTC
For more information on this, see incident 18003.
Find all tokens expiring on a specific date
This script finds tokens that expire on a specific date.
Prerequisites:
- You must know the exact date your instance was upgraded to GitLab 16.0.
To use it:
- In your terminal window, connect to your instance.
- Start a Rails console session with
sudo gitlab-rails console
. - Depending on your needs, copy either the entire
expired_tokens.rb
orexpired_tokens_date_range.rb
script below, and paste it into the console. Change theexpires_at_date
to the date one year after your instance was upgraded to GitLab 16.0. - Press Enter.
- In your terminal window, connect to your instance.
- Depending on your needs, copy either the entire
expired_tokens.rb
orexpired_tokens_date_range.rb
script below, and save it as a file on your instance:- Name it
expired_tokens.rb
. - Change the
expires_at_date
to the date one year after your instance was upgraded to GitLab 16.0. - The file must be accessible to
git:git
.
- Name it
-
Run this command, changing the path to the full path to your
expired_tokens.rb
file:sudo gitlab-rails runner /path/to/expired_tokens.rb
For more information, see the Rails Runner troubleshooting section.
expired_tokens.rb
This script requires you to know the exact date your GitLab instance was upgraded to GitLab 16.0.
# Change this value to the date one year after your GitLab instance was upgraded.
expires_at_date = "2024-05-22"
# Check for expiring personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: expires_at_date).find_each do |token|
puts "Expired Personal Access Token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
# Check for expiring project and group access tokens
PersonalAccessToken.project_access_token.where(expires_at: expires_at_date).find_each do |token|
token.user.members.each do |member|
type = member.is_a?(GroupMember) ? 'Group' : 'Project'
puts "Expired #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
end
Find tokens expiring in a given month
This script finds tokens that expire in a particular month. You don’t need to know the exact date your instance was upgraded to GitLab 16.0. To use it:
- In your terminal window, start a Rails console session with
sudo gitlab-rails console
. - Paste in the entire
tokens_with_no_expiry.rb
script below. If desired, change thedate_range
to a different range. - Press Enter.
- In your terminal window, connect to your instance.
- Copy this entire
tokens_with_no_expiry.rb
script below, and save it as a file on your instance:- Name it
expired_tokens_date_range.rb
. - If desired, change the
date_range
to a different range. - The file must be accessible to
git:git
.
- Name it
-
Run this command, changing
/path/to/expired_tokens_date_range.rb
to the full path to yourexpired_tokens_date_range.rb
file:sudo gitlab-rails runner /path/to/expired_tokens_date_range.rb
For more information, see the Rails Runner troubleshooting section.
expired_tokens_date_range.rb
# This script enables you to search for tokens that expire within a
# certain date range (like 1.month) from the current date. Use it if
# you're unsure when exactly your GitLab 16.0 upgrade completed.
date_range = 1.month
# Check for personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: Date.today .. Date.today + date_range).find_each do |token|
puts "Expired Personal Access Token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
# Check for expiring project and group access tokens
PersonalAccessToken.project_access_token.where(expires_at: Date.today .. Date.today + date_range).find_each do |token|
token.user.members.each do |member|
type = member.is_a?(GroupMember) ? 'Group' : 'Project'
puts "Expired #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
end
Identify dates when many tokens expire
This script identifies dates when most of tokens expire. You can use it in combination with other scripts on this page to identify and extend large batches of tokens that may be approaching their expiration date, in case your team has not yet set up token rotation.
The script returns results in this format:
42 Personal Access Tokens will expire at 2024-06-27
17 Personal Access Tokens will expire at 2024-09-23
3 Personal Access Tokens will expire at 2024-08-13
To use it:
- In your terminal window, start a Rails console session with
sudo gitlab-rails console
. - Paste in the entire
dates_when_most_of_tokens_expire.rb
script. - Press Enter.
- In your terminal window, connect to your instance.
- Copy this entire
dates_when_most_of_tokens_expire.rb
script, and save it as a file on your instance:- Name it
dates_when_most_of_tokens_expire.rb
. - The file must be accessible to
git:git
.
- Name it
-
Run this command, changing
/path/to/dates_when_most_of_tokens_expire.rb
to the full path to yourdates_when_most_of_tokens_expire.rb
file:sudo gitlab-rails runner /path/to/dates_when_most_of_tokens_expire.rb
For more information, see the Rails Runner troubleshooting section.
dates_when_most_of_tokens_expire.rb
PersonalAccessToken
.select(:expires_at, Arel.sql('count(*)'))
.where('expires_at >= NOW()')
.group(:expires_at)
.order(Arel.sql('count(*) DESC'))
.limit(10)
.each do |token|
puts "#{token.count} Personal Access Tokens will expire at #{token.expires_at}"
end
Find tokens with no expiration date
This script finds tokens that lack an expiration date: expires_at
is NULL
. For users
who have not yet upgraded to GitLab version 16.0 or later, the token expires_at
value is NULL
, and can be used to identify tokens to add an expiration date to.
You can use this script in either the Rails console or the Rails Runner:
- In your terminal window, connect to your instance.
- Start a Rails console session with
sudo gitlab-rails console
. - Paste in the entire
tokens_with_no_expiry.rb
script below. - Press Enter.
- In your terminal window, connect to your instance.
- Copy this entire
tokens_with_no_expiry.rb
script below, and save it as a file on your instance:- Name it
tokens_with_no_expiry.rb
. - The file must be accessible to
git:git
.
- Name it
-
Run this command, changing the path to the full path to your
tokens_with_no_expiry.rb
file:sudo gitlab-rails runner /path/to/tokens_with_no_expiry.rb
For more information, see the Rails Runner troubleshooting section.
tokens_with_no_expiry.rb
This script finds tokens without a value set for expires_at
.
# This script finds tokens which do not have an expires_at value set.
# Check for expiring personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: nil).find_each do |token|
puts "Expires_at is nil for Personal Access Token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
# Check for expiring project and group access tokens
PersonalAccessToken.project_access_token.where(expires_at: nil).find_each do |token|
token.user.members.each do |member|
type = member.is_a?(GroupMember) ? 'Group' : 'Project'
puts "Expires_at is nil for #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
end
Extend token lifetime
Delay the expiration of certain tokens with this script.
From GitLab 16.0, all access tokens have an expiration date. After you deploy at least GitLab 16.0, any non-expiring access tokens expire one year from the date of deployment.
If this date is approaching and there are tokens that have not yet been rotated, you can use this script to delay expiration and give users more time to rotate their tokens.
Extend lifetime for specific tokens
This script extends the lifetime of all tokens which expire on a specified date, including:
- Personal access tokens
- Group access tokens
- Project access tokens
Users that have intentionally set a token to expire on the specified date will have their token lifetimes extended as well.
To use the script:
- In your terminal window, start a Rails console session with
sudo gitlab-rails console
. - Paste in the entire
extend_expiring_tokens.rb
script below. If desired, change theexpiring_date
to a different date. - Press Enter.
- In your terminal window, connect to your instance.
- Copy this entire
extend_expiring_tokens.rb
script below, and save it as a file on your instance:- Name it
extend_expiring_tokens.rb
. - If desired, change the
expiring_date
to a different date. - The file must be accessible to
git:git
.
- Name it
-
Run this command, changing
/path/to/extend_expiring_tokens.rb
to the full path to yourextend_expiring_tokens.rb
file:sudo gitlab-rails runner /path/to/extend_expiring_tokens.rb
For more information, see the Rails Runner troubleshooting section.
extend_expiring_tokens.rb
expiring_date = Date.new(2024, 5, 30)
new_expires_at = 6.months.from_now
total_updated = PersonalAccessToken
.not_revoked
.without_impersonation
.where(expires_at: expiring_date.to_date)
.update_all(expires_at: new_expires_at.to_date)
puts "Updated #{total_updated} tokens with new expiry date #{new_expires_at}"