OAuth 2.0 identity provider API

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

GitLab은 API를 제공하여 제3자 서비스가 사용자를 대신하여 GitLab 리소스에 액세스할 수 있게 합니다. OAuth 2.0 프로토콜을 사용합니다.

GitLab을 구성하려면 OAuth 2.0 인증 ID 공급자로서 GitLab 구성을 참조하세요.

이 기능은 doorkeeper Ruby gem에 기반합니다.

Cross-origin resource sharing

일부 /oauth 엔드포인트는 크로스-오리진 리소스 공유 (CORS)를 지원합니다. GitLab 15.1부터 다음 엔드포인트도 CORS 사전 전송 요청을 지원합니다:

  • /oauth/revoke
  • /oauth/token
  • /oauth/userinfo

사전 전송 요청에는 특정 헤더만 사용할 수 있습니다.

예를 들어, X-Requested-With 헤더는 사전 전송 요청에 사용할 수 없습니다.

지원되는 OAuth 2.0 플로우

GitLab은 다음 권한 부여 플로우를 지원합니다:

  • Proof Key for Code Exchange (PKCE)를 사용한 권한 부여 코드: 가장 안전합니다. PKCE가 없으면 모바일 클라이언트에서 클라이언트 비밀을 포함해야 하며, 클라이언트 및 서버 앱에 모두 권장됩니다.
  • 권한 부여 코드: 안전하고 흔한 플로우입니다. 안전한 서버 측 앱에 대한 권장 옵션입니다.
  • 리소스 소유자 암호 자격 증명: 안전하게 호스팅된 제1자 서비스에 사용되어야 합니다. GitLab은 이 플로우의 사용을 권장하지 않습니다.
  • 장치 인증 그랜트 (GitLab 17.1 이상) : 브라우저 액세스 없이 장치 중심으로 하는 안전한 흐름입니다. 권한 부여 플로우를 완료하려면 보조 장치가 필요합니다.

OAuth 2.1에 대한 초안 명세는 특히 암시적 그랜트와 리소스 소유자 암호 자격 증명 플로우를 배제합니다.

이러한 플로우가 모두 어떻게 작동하며 사용 사례에 맞는 올바른 플로우를 선택하려면 OAuth RFC를 참조하세요.

권한 부여 코드 (PKCE 사용 여부에 관계 없음) 플로우는 먼저 /user_settings/applications 페이지에서 application을 등록해야 합니다. 등록하는 동안 적절한 스코프를 활성화하여 application이 액세스할 수 있는 리소스 범위를 제한할 수 있습니다. 생성 후에는 application 자격 증명인 애플리케이션 ID 및 _클라이언트 시크릿_을 얻게 됩니다. 클라이언트 시크릿은 보안 유지해야 합니다. 응용 프로그램 아키텍처가 허용하는 경우 _애플리케이션 ID_도 비밀로 유지하는 것이 유익합니다.

GitLab의 스코프 목록은 공급자 설명서를 참조하세요.

CSRF 공격 방지

OAuth 사양은 “사용자 에이전트에 안전하게 바인딩된 ‘상태’ 매개변수에 들어있는 일회용 CSRF 토큰을 사용하는 것”을 권장하여 리디렉션 기반 플로우를 보호합니다. 이를 통해 CSRF 공격을 방지할 수 있습니다.

프로덕션에서 HTTPS 사용

프로덕션 환경에서는 redirect_uri에 HTTPS를 사용하세요. 개발 환경에서는 GitLab이 안전하지 않은 HTTP 리디렉트 URI를 허용합니다.

OAuth 2.0은 보안을 완전히 전송 계층에 의존하기 때문에 보호되지 않은 URI를 사용해서는 안 됩니다. 자세한 정보는 OAuth 2.0 RFCOAuth 2.0 위협 모델 RFC를 참조하세요.

다음 섹션에서 각 플로우로 인증을 얻는 방법에 대한 자세한 지침을 찾을 수 있습니다.

Proof Key for Code Exchange (PKCE)를 사용한 권한 부여 코드

PKCE RFC에는 권한 부여 요청부터 액세스 토큰까지의 자세한 플로우 설명이 포함되어 있습니다. 다음 단계에서는 이 플로우의 구현을 설명합니다.

PKCE 플로우, PKCE라고 함으로써, 사용자의 클라이언트 비밀을 전혀 요구하지 않고 공개 클라이언트에서 클라이언트 자격 증명을 안전하게 수행할 수 있게 합니다. 이는 사용자에게 비밀을 숨기는 것이 기술적으로 불가능한 단일 페이지 JavaScript 애플리케이션이나 기타 클라이언트 측 앱에 있어서 PKCE 플로우가 유리합니다.

플로우를 시작하기 전에 STATE, CODE_VERIFIER, CODE_CHALLENGE를 생성하세요.

  • STATE는 클라이언트가 요청과 콜백 사이의 상태를 유지하는 데 사용하는 예측할 수 없는 값입니다. 또한 CSRF 토큰으로 사용해야 합니다.
  • CODE_VERIFIER는 길이가 43에서 128자 사이인 무작위 문자열입니다. A-Z, a-z, 0-9, -, ., _, ~ 문자를 사용합니다.
  • CODE_CHALLENGECODE_VERIFIER의 SHA256 해시의 URL-세이프한 base64로 인코딩된 문자열입니다.
    • 인코딩하기 전에 SHA256 해시는 이진 형식이어야 합니다.
    • Ruby에서는 Base64.urlsafe_encode64(Digest::SHA256.digest(CODE_VERIFIER), padding: false)으로 설정할 수 있습니다.
    • 참고로, Ruby 스니펫을 사용하여 CODE_VERIFIER 문자열이 ks02i3jdikdo2k0dkfodf3m39rjfjsdk0wk349rj3jrhf인 경우, 위의 스니펫을 사용하여 해싱 및 인코딩하면 CODE_CHALLENGE 문자열이 2i0WFA-0AerkjQm4X4oDEhqA17QIAKNjXpagHBXmO_U가 됩니다.
  1. 권한 부여 코드를 요청하세요. 이를 위해서는 다음 쿼리 매개변수로 사용자를 /oauth/authorize 페이지로 리디렉션해야 합니다:

    https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&state=STATE&scope=REQUESTED_SCOPES&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
    

    이 페이지는 사용자에게 REQUESTED_SCOPES에 지정된 범위로 계정에 액세스할 수 있는 앱 요청을 승인할 것인지를 물어봅니다. 사용자는 지정된 REDIRECT_URI로 다시 리디렉션됩니다. 스코프 매개변수는 사용자와 관련된 스코프의 공백으로 구분된 목록입니다. 예를 들어, scope=read_user+profileread_userprofile 스코프를 요청합니다. 리디렉트에는 권한 code가 포함됩니다. 예:

    https://example.com/oauth/redirect?code=1234567890&state=STATE
    
  2. 이전 요청에서 반환된 권한 code(다음 예제에서 RETURNED_CODE로 표시함)로, 모든 HTTP 클라이언트를 사용하여 access_token을 요청할 수 있습니다. 다음 예제는 Ruby의 rest-client를 사용합니다.

    parameters = 'client_id=APP_ID&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER'
    RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    응답 예시:

    {
     "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54",
     "token_type": "bearer",
     "expires_in": 7200,
     "refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1",
     "created_at": 1607635748
    }
    
  3. 새로운 access_token을 검색하려면 refresh_token 매개변수를 사용하세요. access_token 자체가 만료된 후에도 refresh_token을 사용할 수 있습니다. 이 요청은 다음을 수행합니다:

    • 기존 access_tokenrefresh_token을 무효화합니다.
    • 응답에서 새 토큰을 보냅니다.
      parameters = 'client_id=APP_ID&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER'
      RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    응답 예시:

    {
      "access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68",
      "token_type": "bearer",
      "expires_in": 7200,
      "refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f",
      "created_at": 1628711391
    }
    

참고: redirect_uri는 원래 인가 요청에서 사용된 redirect_uri와 일치해야 합니다.

이제 액세스 토큰으로 API에 요청을 보낼 수 있습니다.

인가 코드 플로우

note
상세한 플로우 설명은 RFC 사양을 확인하십시오.

인가 코드 플로우는 기본적으로 PKCE를 사용한 인가 코드 플로우와 동일합니다.

플로우를 시작하기 전에 STATE를 생성합니다. 이는 클라이언트가 요청과 콜백 사이의 상태를 유지하기 위해 사용되는 예측할 수 없는 값입니다. 또한 CSRF 토큰으로 사용되어야 합니다.

  1. 인가 코드 요청. 이를 위해 사용자를 /oauth/authorize 페이지로 리디렉션하여 다음 쿼리 매개변수를 함께 보냅니다:

    https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&state=STATE&scope=REQUESTED_SCOPES
    

    해당 페이지에서 사용자에게 REQUESTED_SCOPES에 지정된 범위를 기반으로 앱이 계정에 액세스를 요청하는 것을 승인할 것인지 물어봅니다. 사용자는 지정된 REDIRECT_URI로 다시 리디렉션됩니다. scope parameter는 사용자와 관련된 범위의 공백으로 구분된 목록입니다. 예를 들어 scope=read_user+profileread_userprofile 범위를 요청합니다. 리디렉션에는 인가 code가 포함됩니다. 예를 들어:

    https://example.com/oauth/redirect?code=1234567890&state=STATE
    
  2. 이전 요청에서 반환된 인가 code (다음 예에서 RETURNED_CODE로 표시됨)를 사용하여 HTTP 클라이언트로 access_token을 요청할 수 있습니다. 다음 예에서는 Ruby의 rest-client를 사용합니다.

    parameters = 'client_id=APP_ID&client_secret=APP_SECRET&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI'
    RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    응답 예시:

    {
     "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54",
     "token_type": "bearer",
     "expires_in": 7200,
     "refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1",
     "created_at": 1607635748
    }
    
  3. 새로운 access_token을 검색하려면 refresh_token 매개변수를 사용합니다. access_token 자체가 만료된 후에도 refresh_token을 사용할 수 있습니다. 이 요청은 다음과 같은 동작을 수행합니다:

    • 기존의 access_tokenrefresh_token을 무효화합니다.
    • 응답에서 새 토큰을 보냅니다.
      parameters = 'client_id=APP_ID&client_secret=APP_SECRET&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI'
      RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    응답 예시:

    {
      "access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68",
      "token_type": "bearer",
      "expires_in": 7200,
      "refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f",
      "created_at": 1628711391
    }
    
note
redirect_uri는 원본 인가 요청에서 사용된 redirect_uri와 일치해야 합니다.

이제 반환된 access_token을 사용하여 API에 요청을 보낼 수 있습니다.

디바이스 인가 그랜트 플로우

플래그: 이 기능의 가용성은 기능 플래그에 의해 제어됩니다. 자세한 정보는 기록을 확인하십시오.

note
디바이스 인가 그랜트 플로우에 대한 자세한 설명, 디바이스 인가 요청부터 브라우저 로그인에 대한 토큰 응답까지의 내용은 RFC 사양을 확인하십시오.

디바이스 인가 그랜트 플로우를 사용하면 브라우저 상호작용이 불가능한 입력 제한 장치에서 GitLab 식별을 안전하게 인증할 수 있습니다.

이로써 디바이스 인가 그랜트 플로우는 헤드리스 서버 또는 UI가 없거나 제한적인 장치에서 GitLab 서비스를 사용하려는 사용자에게 이상적입니다.

  1. 디바이스 인가를 요청하려면, 입력이 제한된 디바이스 클라이언트에서 https://gitlab.example.com/oauth/authorize_device로 요청을 보냅니다. 예를 들어:

      parameters = 'client_id=UID&scope=read'
      RestClient.post 'https://gitlab.example.com/oauth/authorize_device', parameters
    

    성공적인 요청 후, 사용자에게 verification_uri를 포함한 응답이 반환됩니다. 예를 들어:

    {
        "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
        "user_code": "0A44L90H",
        "verification_uri": "https://gitlab.example.com/oauth/device",
        "verification_uri_complete": "https://gitlab.example.com/oauth/device?user_code=0A44L90H",
        "expires_in": 300,
        "interval": 5
    }
    
  2. 디바이스 클라이언트는 응답에서 반환된 user_codeverification_uri를 요청한 사용자에게 표시합니다. 그 사용자는 그런 다음 브라우저 액세스가 가능한 보조 장치에서:
    1. 제공된 URI로 이동합니다.
    2. 사용자 코드를 입력합니다.
    3. 알림대로 인증을 완료합니다.
  3. verification_uriuser_code를 표시한 직후, 디바이스 클라이언트는 초기 응답에서 반환된 연관된 device_code로 토큰 엔드포인트를 폴링하기 시작합니다.

    parameters = 'grant_type=urn:ietf:params:oauth:grant-type:device_code
    &device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
    &client_id=1406020730'
    RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    
  4. 디바이스 클라이언트는 토큰 엔드포인트에서 응답을 받습니다. 인가가 성공했다면 성공 응답이 반환되고, 그렇지 않으면 오류 응답이 반환됩니다. 잠재적인 오류 응답은 OAuth 인가 프레임워크 액세스 토큰 오류 응답 또는 여기에 설명된 디바이스 인가 그랜트 플로우에 특정한 오류 응답으로 분류됩니다. 디바이스 플로우에 특정한 이러한 오류 응답에 대한 자세한 내용은 관련 디바이스 인가 그랜트를 위한 RFC 사양인가 토큰을 위한 RFC 사양을 참조하십시오.

    응답 예시:

    {
      "error": "authorization_pending",
      "error_description": "..."
    }
    

    이 응답을 받으면, 디바이스 클라이언트는 계속 폴링합니다.

    폴링 간격이 너무 짧으면 속도 조절 오류 응답이 반환됩니다. 예를 들어:

     {
       "error": "slow_down",
       "error_description": "..."
     }
    

    이 응답을 받으면, 디바이스 클라이언트는 폴링 속도를 줄이고 새로운 속도에서 계속 폴링합니다.

    디바이스 코드의 만료가 완료되기 전에 인증이 완료되지 않으면 만료된 토큰 오류 응답이 반환됩니다. 예를 들어:

    {
      "error": "expired_token",
      "error_description": "..."
    }
    

    이 시점에서 디바이스 클라이언트는 멈추고 새로운 디바이스 인가 요청을 시작해야 합니다.

    인가 요청이 거부된 경우, 액세스 거부 오류 응답이 반환됩니다. 예를 들어:

    {
      "error": "access_denied",
      "error_description": "..."
    }
    

    인증 요청이 거부되었습니다. 사용자는 자격 증명을 검증하거나 시스템 관리자에게 문의해야 합니다.

  5. 사용자의 성공적인 인증 이후, 성공 응답이 반환됩니다:

    {
        "access_token": "TOKEN",
        "token_type": "Bearer",
        "expires_in": 7200,
        "scope": "read",
        "created_at": 1593096829
    }
    

이제 디바이스 인증 플로우가 완료되었습니다. 반환된 access_token은 GitLab에 제공되어 GitLab 리소스에 액세스할 때 사용자 식별을 인증하는 데 사용될 수 있습니다. (예: HTTPS로 복제하거나 API에 액세스하는 경우)

클라이언트 측의 디바이스 플로우를 구현하는 샘플 응용 프로그램은 다음에서 찾을 수 있습니다: https://gitlab.com/johnwparent/git-auth-over-https.

리소스 소유자 비밀번호 자격 증명 플로우

주의: 자세한 흐름 설명은 RFC 사억서를 확인하세요.

주의: 리소스 소유자 비밀번호 자격 증명은 이중 인증이 활성화된 사용자에게 비활성화됩니다. 이러한 사용자는 개인 엑세스 토큰을 사용하여 API에 액세스할 수 있습니다.

주의: GitLab 인스턴스에서 비밀번호 자격 증명 플로우를 지원하려면 HTTP(S)를 통한 Git의 비밀번호 인증 허용 체크박스가 선택되었는지 확인하세요.

이 플로우에서 토큰은 리소스 소유자 자격 증명(사용자 이름 및 비밀번호)과 교환하여 요청됩니다.

리소스 소유자와 클라이언트 사이에 높은 신뢰도가 있는 경우에만 자격 증명을 사용해야 합니다. 예를 들어, 클라이언트가 장치 운영 체제의 일부이거나 고도로 권한이 있는 애플리케이션인 경우입니다. 다른 인가 그랜트 유형을 사용할 수 없는 경우(예: 인가 코드)에도 사용해야 합니다.

경고: 사용자의 자격 증명을 절대로 저장해서는 안 되며, 클라이언트가 신뢰할 수 있는 환경에 배포된 경우에만 이 그랜트 유형을 사용해야 합니다. 대부분의 경우에는 개인 엑세스 토큰이 더 나은 선택입니다.

비록 이 그랜트 유형은 리소스 소유자 자격 증명에 대한 직접적인 클라이언트 액세스를 요구하지만, 리소스 소유자 자격 증명은 단일 요청에 사용되고 액세스 토큰으로 교환됩니다. 이 그랜트 유형은 장기적인 액세스 토큰이나 갱신 토큰과 자격 증명을 교환함으로써 클라이언트가 나중에 리소스 소유자 자격 증명을 저장하지 않아도 되도록 할 수 있습니다.

액세스 토큰을 요청하려면 다음과 같은 매개변수로 /oauth/token에 POST 요청을 수행해야 합니다.

{
  "grant_type"    : "password",
  "username"      : "user@example.com",
  "password"      : "secret"
}

cURL 요청 예제:

echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "@auth.txt" --request POST "https://gitlab.example.com/oauth/token"

등록된 OAuth 애플리케이션을 사용하여 이 그랜트 플로우도 사용할 수 있습니다. 이 경우 애플리케이션의 client_idclient_secret을 사용하여 HTTP 기본 인증을 수행해야 합니다.

echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "@auth.txt" --user client_id:client_secret \
     --request POST "https://gitlab.example.com/oauth/token"

그럼, 액세스 토큰을 포함한 응답을 받을 수 있습니다.

{
  "access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
  "token_type": "bearer",
  "expires_in": 7200
}

액세스 토큰의 기본 범위는 api로, 완전한 읽기/쓰기 액세스를 제공합니다.

테스트를 위해 oauth2 Ruby gem을 사용할 수 있습니다.

client = OAuth2::Client.new('the_client_id', 'the_client_secret', :site => "https://example.com")
access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token

액세스 토큰으로 GitLab API에 액세스

액세스 토큰을 사용하면 사용자를 대신하여 API에 요청을 보낼 수 있습니다. 액세스 토큰을 GET 매개변수 또는 인가 헤더로 전달할 수 있습니다.

GET https://gitlab.example.com/api/v4/user?access_token=OAUTH-TOKEN

또는 다음과 같이 헤더에 토큰을 넣을 수 있습니다.

curl --header "Authorization: Bearer OAUTH-TOKEN" "https://gitlab.example.com/api/v4/user"

액세스 토큰으로 Git을 HTTPS를 통해 액세스

read_repository 또는 write_repository 범위를 가진 토큰은 HTTPS를 통해 Git에 액세스할 수 있습니다. 토큰을 암호로 사용합니다. 사용자 이름을 임의 값으로 설정할 수 있습니다. oauth2를 사용해야 합니다.

https://oauth2:<your_access_token>@gitlab.example.com/project_path/project_name.git

또는 Git 자격 증명 도우미를 사용하여 OAuth로 GitLab에 인증할 수도 있습니다. 이렇게 하면 OAuth 토큰을 자동으로 갱신할 수 있습니다.

토큰 정보 검색

토큰의 세부 정보를 확인하려면 Doorkeeper gem이 제공하는 token/info 엔드포인트를 사용해야 합니다. 자세한 정보는 /oauth/token/info를 참조하세요.

액세스 토큰을 매개변수로 제공해야합니다.

  • 매개변수로:

    GET https://gitlab.example.com/oauth/token/info?access_token=<OAUTH-TOKEN>
    
  • 인가 헤더로:

    curl --header "Authorization: Bearer <OAUTH-TOKEN>" "https://gitlab.example.com/oauth/token/info"
    

다음은 예제 응답입니다.

{
    "resource_owner_id": 1,
    "scope": ["api"],
    "expires_in": null,
    "application": {"uid": "1cb242f495280beb4291e64bee2a17f330902e499882fe8e1e2aa875519cab33"},
    "created_at": 1575890427
}

폐기된 필드

scopes 필드와 expires_in_seconds 필드가 응답에 포함되지만 현재는 폐기되었습니다. scopes 필드는 scope의 별명이고, expires_in_seconds 필드는 expires_in의 별명입니다. 더 자세한 정보는 Doorkeeper API 변경 사항을 참조하세요.

토큰 취소

토큰을 취소하려면 revoke 엔드포인트를 사용해야 합니다. API는 성공을 나타내기 위해 200 응답 코드와 빈 JSON 해시를 반환합니다.

parameters = 'client_id=APP_ID&client_secret=APP_SECRET&token=TOKEN'
RestClient.post 'https://gitlab.example.com/oauth/revoke', parameters

OAuth 2.0 토큰 및 GitLab 레지스트리

표준 OAuth 2.0 토큰은 다음과 같은 GitLab 레지스트리의 다양한 액세스를 지원합니다.