You can set permissions on a GitHub workflow by specifying the permissions
key either at the top level of your workflow file or within individual jobs. This controls the access level granted to the GITHUB_TOKEN
.
Why Set Permissions?
GitHub Actions workflows automatically generate a unique GITHUB_TOKEN
for each job run. This token is used to authenticate requests made to the GitHub API on behalf of your repository. By default, this token has a limited set of permissions. Setting permissions explicitly allows you to:
- Increase permissions for specific tasks that require broader access (e.g., creating releases, adding comments).
- Decrease permissions to follow the principle of least privilege, enhancing security by restricting what the workflow token can do.
Where to Set Permissions
As stated in the documentation, you can use permissions either as a top-level key, to apply to all jobs in the workflow, or within specific jobs.
1. Top-Level Workflow Permissions
Setting permissions
at the top level applies those access rights as the default for all jobs within that workflow. This is useful when most jobs require a similar set of permissions.
# .github/workflows/my-workflow.yml
name: My Workflow
permissions:
contents: read # Default permission for all jobs is read-only for contents
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4 # Uses the default 'contents: read' permission
deploy:
runs-on: ubuntu-latest
# This job also inherits 'contents: read' but needs write access for releases
permissions:
contents: write # Overrides the workflow-level permission for THIS job
steps:
- name: Create Release
# This step would need write permission for contents
# ... steps to create release ...
In this example, contents: read
is the default for both the build
and deploy
jobs. However, the deploy
job specifically overrides the contents
permission to write
, allowing it to perform actions like creating releases.
2. Job-Level Permissions
Setting permissions
within a specific job applies those access rights only to that particular job. When you add the permissions key within a specific job, all actions and run commands within that job that use the GITHUB_TOKEN gain the access rights you specify. This overrides any permissions set at the workflow level for that specific job. This is ideal when only one job requires elevated or reduced permissions.
# .github/workflows/another-workflow.yml
name: Another Workflow
# No top-level permissions defined - uses default permissions
on: [push]
jobs:
lint:
runs-on: ubuntu-latest
# No permissions key here - uses default GITHUB_TOKEN permissions
steps:
- name: Run Linter
# ... linter steps ...
comment:
runs-on: ubuntu-latest
permissions:
issues: write # Only this job gets write access for issues
steps:
- name: Add Issue Comment
# This step can add comments to issues using the token
# ... steps to add comment ...
In this workflow, the lint
job uses the default GITHUB_TOKEN
permissions (which are typically read-only for most scopes). The comment
job, however, is granted explicit write
permission for issues
, allowing it to interact with issues via the API.
Understanding Permission Scopes and Levels
Permissions are set using key-value pairs where the key is the scope (e.g., contents
, issues
, pull-requests
) and the value is the access level (read
, write
, or none
).
Here are some common scopes and their purposes:
Scope | Description | Access Levels |
---|---|---|
actions |
Manage GitHub Actions workflow runs. | read , write , none |
contents |
Manage repository contents, commits, tags. | read , write , none |
deployments |
Manage deployments. | read , write , none |
issues |
Manage issues and issue comments. | read , write , none |
pull-requests |
Manage pull requests and PR comments. | read , write , none |
packages |
Manage GitHub Packages. | read , write , none |
statuses |
Manage commit statuses. | read , write , none |
checks |
Manage checks. | read , write , none |
discussions |
Manage repository discussions. | read , write , none |
id-token |
Request an OIDC ID token. | write , none |
metadata |
Always read-only; required by actions/checkout. | read (default) |
You can set permissions for multiple scopes:
permissions:
contents: read
issues: write
pull-requests: write
Best Practices
- Least Privilege: Grant only the permissions absolutely necessary for the job or workflow to complete its tasks.
- Job-Level Specificity: Use job-level permissions when only one job needs specific access, overriding the workflow-level defaults.
- Review Defaults: Understand the default permissions your repository settings apply to the
GITHUB_TOKEN
. You can often reduce overall permissions at the repository or organization level for increased security.
By strategically using the permissions
key at the workflow or job level, you can precisely control the GITHUB_TOKEN
's capabilities, balancing functionality with security.