tech

How to Generate GitLab Personal Access Tokens and API Keys

1253 words7 min read
How to Generate GitLab Personal Access Tokens and API Keys
Authors

In this guide, I'll show you how to create GitLab personal access tokens, configure deploy keys, and use them for API authentication and CI/CD pipelines.

I needed GitLab tokens for automating deployments, accessing the GitLab API, and integrating with third-party services for my DevOps pipeline.

First Approach (Personal Access Token)


Step 1: Access Token Settings

  • Log in to your GitLab account
  • Click your profile picture in the top right
  • Select Preferences
  • In the left sidebar, click Access Tokens

Step 2: Create New Token

  • Click Add new token
  • Enter a token name (e.g., "API Integration")
  • Set expiration date (optional, for security choose 90 days)
  • Select scopes based on your needs:
    • api - Full API access
    • read_user - Read user information
    • read_api - Read API access
    • read_registry - Read container registry
    • write_repository - Push to repositories
    • maintainer_access - Maintain projects
  • Click Create personal access token

GitLab Access Token Settings

Step 3: Copy Your Token

  • Important: Copy your token immediately
  • GitLab won't show it again after you leave the page
  • Store it securely in environment variables or password manager

Step 4: Use Token for API Requests

  • Add token to API requests as a header:
curl --header "PRIVATE-TOKEN: YOUR_GITLAB_TOKEN" \
  https://gitlab.com/api/v4/user
  • Or as a query parameter:
curl "https://gitlab.com/api/v4/user?private_token=YOUR_GITLAB_TOKEN"
  • In JavaScript:
const token = process.env.GITLAB_TOKEN;
const response = await fetch('https://gitlab.com/api/v4/user', {
  headers: {
    'PRIVATE-TOKEN': token
  }
});
const data = await response.json();
console.log(data);

Step 5: Use Token for Git Operations

  • Clone private repositories:
git clone https://oauth2:YOUR_GITLAB_TOKEN@gitlab.com/username/repo.git
  • Configure Git to use token:
git config --global credential.helper store
echo "https://oauth2:YOUR_GITLAB_TOKEN@gitlab.com" > ~/.git-credentials


Second Approach (OAuth Application)


Step 1: Create OAuth Application

  • Go to Admin Area (visible if you have admin access)
  • Select Applications under System
  • Click New application
  • Fill in the application details:
    • Name: Your app name
    • Redirect URI: https://yourapp.com/callback
    • Scopes: Select required scopes
    • Check Trusted if this is your own application
  • Click Save application

Step 2: Get Credentials

  • You'll receive:
    • Application ID
    • Secret
  • Store both securely in environment variables

Step 3: Create Authorization URL

  • Build the authorization URL:
https://gitlab.com/oauth/authorize?client_id=$APPLICATION_ID&redirect_uri=$REDIRECT_URI&response_type=code&scope=$SCOPE&state=$STATE
  • Example:
https://gitlab.com/oauth/authorize?client_id=abc123&redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback&response_type=code&scope=read_user&state=random_state

Note: URL encode your redirect URI

Step 4: Exchange Code for Token

  • When users authorize, they're redirected with a code parameter
  • Exchange it for an access token:
curl --request POST https://gitlab.com/oauth/token \
  -d "client_id=$APPLICATION_ID" \
  -d "client_secret=$SECRET" \
  -d "code=$CODE" \
  -d "grant_type=authorization_code" \
  -d "redirect_uri=$REDIRECT_URI"

Step 5: Handle Token Response

  • Response includes:
{
  "access_token": "de6780bc506a0446529482c321cdf285",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "8257e65c97f3a53ce93caf05cb0",
  "created_at": 1622548961
}
  • Use access_token for API requests
  • Store refresh_token for token renewal

Third Approach (Deploy Keys)


Step 1: Generate SSH Key Pair

ssh-keygen -t rsa -b 4096 -C "deploy@yourapp.com" -f deploy_key

Step 2: Add Deploy Key to Project

  • Go to your GitLab project
  • Select Settings > Repository
  • Expand Deploy keys
  • Click Add deploy key
  • Enter key title (e.g., "CI/CD Deploy Key")
  • Paste your public key (from deploy_key.pub)
  • Check Grant write permissions to this key if needed
  • Click Add key

Step 3: Use Deploy Key in CI/CD

  • Add to your .gitlab-ci.yml:
deploy:
  stage: deploy
  before_script:
    - mkdir -p ~/.ssh
    - ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
    - echo "$DEPLOY_KEY" | tr -d '\r' | ssh-add - > /dev/null
  script:
    - git clone git@gitlab.com:username/repo.git
    - cd repo && ./deploy.sh
  only:
    - main
  • Add private key as CI/CD variable: Settings > CI/CD > Variables
  • Name: DEPLOY_KEY, Value: contents of deploy_key

API Request Examples


Get Current User

curl --header "PRIVATE-TOKEN: YOUR_TOKEN" \
  https://gitlab.com/api/v4/user

List Projects

curl --header "PRIVATE-TOKEN: YOUR_TOKEN" \
  https://gitlab.com/api/v4/projects?per_page=10

Create Issue

curl --request POST --header "PRIVATE-TOKEN: YOUR_TOKEN" \
  --data "title=New%20issue&description=Test" \
  https://gitlab.com/api/v4/projects/1/issues

Trigger Pipeline

curl --request POST --header "PRIVATE-TOKEN: YOUR_TOKEN" \
  https://gitlab.com/api/v4/projects/1/pipeline?ref=main

Get Repository Files

curl --header "PRIVATE-TOKEN: YOUR_TOKEN" \
  https://gitlab.com/api/v4/projects/1/repository/tree

Python Implementation Example

import os
import requests
from dotenv import load_dotenv

load_dotenv()

GITLAB_URL = 'https://gitlab.com'
GITLAB_TOKEN = os.getenv('GITLAB_TOKEN')
PROJECT_ID = os.getenv('GITLAB_PROJECT_ID')

headers = {
    'PRIVATE-TOKEN': GITLAB_TOKEN,
    'Content-Type': 'application/json'
}

def get_project_info():
    """Get project details"""
    response = requests.get(
        f'{GITLAB_URL}/api/v4/projects/{PROJECT_ID}',
        headers=headers
    )
    return response.json()

def create_issue(title, description):
    """Create a new issue"""
    data = {
        'title': title,
        'description': description
    }
    response = requests.post(
        f'{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/issues',
        headers=headers,
        json=data
    )
    return response.json()

def list_pipelines():
    """List recent pipelines"""
    response = requests.get(
        f'{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/pipelines',
        headers=headers
    )
    return response.json()

def trigger_pipeline(ref='main'):
    """Trigger a pipeline on specified branch"""
    data = {'ref': ref}
    response = requests.post(
        f'{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/pipeline',
        headers=headers,
        json=data
    )
    return response.json()

if __name__ == '__main__':
    print("Project:", get_project_info()['name'])
    new_issue = create_issue('Test Issue', 'This is a test')
    print("Created issue:", new_issue['id'])
    print("Pipelines:", list_pipelines())

Node.js Implementation Example

require('dotenv').config();
const axios = require('axios');

const GITLAB_URL = 'https://gitlab.com';
const GITLAB_TOKEN = process.env.GITLAB_TOKEN;
const PROJECT_ID = process.env.GITLAB_PROJECT_ID;

const client = axios.create({
  baseURL: `${GITLAB_URL}/api/v4`,
  headers: {
    'PRIVATE-TOKEN': GITLAB_TOKEN
  }
});

async function getProjectInfo() {
  try {
    const response = await client.get(`/projects/${PROJECT_ID}`);
    return response.data;
  } catch (error) {
    console.error('Error:', error.response.data);
  }
}

async function createIssue(title, description) {
  try {
    const response = await client.post(
      `/projects/${PROJECT_ID}/issues`,
      { title, description }
    );
    return response.data;
  } catch (error) {
    console.error('Error:', error.response.data);
  }
}

async function listPipelines() {
  try {
    const response = await client.get(`/projects/${PROJECT_ID}/pipelines`);
    return response.data;
  } catch (error) {
    console.error('Error:', error.response.data);
  }
}

async function triggerPipeline(ref = 'main') {
  try {
    const response = await client.post(
      `/projects/${PROJECT_ID}/pipeline`,
      { ref }
    );
    return response.data;
  } catch (error) {
    console.error('Error:', error.response.data);
  }
}

module.exports = { getProjectInfo, createIssue, listPipelines, triggerPipeline };

GitLab CI/CD Token Example


Add this to .gitlab-ci.yml to use tokens in pipelines:

stages:
  - build
  - deploy

variables:
  REGISTRY_URL: "registry.gitlab.com"
  CI_REGISTRY_USER: "oauth2"

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $REGISTRY_URL
    - docker build -t $REGISTRY_URL/$CI_PROJECT_PATH:latest .
    - docker push $REGISTRY_URL/$CI_PROJECT_PATH:latest
  only:
    - main

deploy:
  stage: deploy
  image: alpine:latest
  script:
    - echo "Deploying to production..."
    - curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
        https://gitlab.com/api/v4/projects/$CI_PROJECT_ID
  only:
    - main

Security Best Practices

  • Never commit tokens to repositories - use CI/CD variables
  • Rotate tokens regularly - regenerate quarterly
  • Use limited scopes - only grant necessary permissions
  • Enable two-factor authentication - protect your account
  • Revoke unused tokens - regularly audit and clean up
  • Use deploy keys for read-only repository access
  • Store secrets in CI/CD variables - not in code or .env files
  • Monitor token usage - check activity logs for unusual access
  • Implement expiration dates - shorter expiry improves security
  • Check GitLab Token Security

Troubleshooting

IssueSolution
"401 Unauthorized"Verify token is correct and not expired
Token not working in CI/CDAdd as CI/CD variable, not hardcoded
Deploy key permission deniedEnsure key has write access if needed
Token scope insufficientCreate new token with broader scope
Lost tokenRevoke old token, create new one

Ready to integrate? Check out the GitLab API Documentation and GitLab CI/CD Documentation for complete reference.

Tags

#gitlab#ci-cd#api#git-authentication