OAuth 2.0 Identity Provider API

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

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

GitLab를 구성하려면 GitLab을 OAuth 2.0 인증 ID 공급자로 구성을 참조하십시오.

이 기능은 doorkeeper Ruby gem에 기반하고 있습니다.

Cross-origin resource sharing

많은 /oauth 엔드포인트가 Cross-Origin Resource Sharing(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 없이는 모바일 클라이언트에서 클라이언트 비밀을 포함해야 하며 클라이언트 및 서버 앱에서 모두 권장됩니다.
  • 인가 코드: 안전하고 일반적인 플로우입니다. 안전한 서버 측 앱에 권장되는 옵션입니다.
  • 리소스 소유자 비밀 자격 증명: 안전하게 호스팅된 일급 서비스에만 사용해야 합니다. GitLab은 이 플로우의 사용을 권장하지 않습니다.

디바이스 인가 그랜트는 지원되지 않습니다. 이슈 332682에서 지원 추가를 제안합니다.

OAuth 2.1의 초안 명세서에서는 명시적으로 암시적 그랜트 및 리소스 소유자 비밀 자격 증명 플로우를 제외하고 있습니다.

모든 이러한 플로우가 어떻게 작동하는지에 대해 알아보고 사용 사례에 맞는 올바른 플로우를 선택하려면 OAuth RFC를 참조하십시오.

인가 코드(Proof Key for Code Exchange 포함) 플로우를 사용하려면 먼저 /user_settings/applications 페이지에서 application을 등록해야 합니다. 등록 중에 적절한 스코프를 활성화하여 application이 액세스할 수 있는 리소스 범위를 제한할 수 있습니다. 생성 후에는 application 자격 증명(애플리케이션 ID 및 클라이언트 시크릿)을 획들할 수 있습니다. 클라이언트 시크릿은 안전하게 보관해야 합니다. 애플리케이션 아키텍처가 허용하는 경우 애플리케이션 ID를 비밀로 유지하는 것도 유리합니다.

GitLab의 스코프 디렉터리은 공급자 설명서를 참조하십시오.

CSRF 공격 방지

리다이렉트 기반 플로우를 보호하기 위해 OAuth명세는 “사용자 에이전트에 안전하게 바인딩된 상태 매개변수에 포함된 일회용 CSRF 토큰” 사용을 권장합니다. 이는 /oauth/authorize 엔드포인트에 대한 각 요청마다 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, Proof Key for Code Exchange)는 사용자에게 클라이언트 비밀을 전혀 알 필요가 없이 공개 클라이언트에서 클라이언트 자격 증명을 액세스 토큰으로 안전하게 교환할 수 있게 합니다. 이로 인해 PKCE 플로우가 사용자에게 비밀을 숨길 수 없는 싱글 페이지 JavaScript 애플리케이션 또는 기타 클라이언트 측 앱에 유리합니다.

플로우를 시작하기 전에 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=앱_ID&redirect_uri=리디렉트_URI&response_type=code&state=STATE&scope=요청된_스코프&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
    

    이 페이지에서 사용자에게 요청된_스코프에 지정된 스코프에 따라 계정에 액세스하기 위한 앱의 요청을 승인할 것인지 묻습니다. 사용자는 지정된 리디렉트_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
    }
    
note
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=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와 일치해야 합니다.

이제 반환된 액세스 토큰을 사용하여 API에 요청을 보낼 수 있습니다.

리소스 소유자 암호 자격 증명 플로우

note
세부 플로우 설명은 RFC 사언를 확인하세요.
note
리소스 소유자 암호 자격 증명은 이중 인증을 사용하는 사용자에게는 비활성화되어 있습니다. 이러한 사용자는 개인 액세스 토큰을 사용하여 API에 액세스할 수 있습니다.
note
리소스 소유자 암호 자격 증명 플로우를 지원하려면 GitLab 인스턴스의 Allow password authentication for Git over HTTP(S) 확인란이 선택되어 있어야 합니다.

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

이러한 자격 증명은 다음 경우에만 사용해야 합니다:

  • 리소스 소유자와 클라이언트 간에 높은 신뢰 수준이 있는 경우. 예를 들어, 클라이언트가 장치 운영 체제의 일부이거나 고도로 권한이 있는 애플리케이션인 경우.
  • 다른 인가 부여 유형을 사용할 수 없는 경우(예: 인가 코드와 같은).
caution
사용자 자격 증명을 절대로 저장하고, 클라이언트가 신뢰하는 환경에 배포된 경우에만 이 부여 유형을 사용하세요. 대부분의 경우에는 개인 액세스 토큰이 더 나은 선택입니다.

이 부여 유형은 리소스 소유자 자격 증명에 대한 직접적인 클라이언트 액세스를 요구하지만, 리소스 소유자 자격 증명은 단일 요청에 사용되며 액세스 토큰으로 교환됩니다. 이 부여 유형을 사용하면 클라이언트가 리소스 소유자 자격 증명을 장기로 사용할 수 있는 액세스 토큰이나 리프레시 토큰과 교환함으로써 리소스 소유자 자격 증명을 미래 사용을 위해 저장할 필요가 없어질 수 있습니다.

액세스 토큰을 요청하려면 다음 매개변수를 사용하여 /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

access token으로 GitLab API에 액세스

access token을 사용하면 사용자를 대신하여 API에 요청을 보낼 수 있습니다. 토큰은 GET 매개변수로 전달할 수도 있습니다:

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

또는 헤더의 Authorization에 토큰을 넣을 수도 있습니다:

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

access token으로 HTTPS를 통한 Git에 액세스

read_repository 또는 write_repository 범위를 가진 토큰은 HTTPS를 통해 Git에 액세스할 수 있습니다. 토큰을 비밀번호로 사용하세요. 사용자 이름은 꼭 oauth2여야 합니다:

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

대신, OAuth로 GitLab에 인증하는 데 Git 자격 증명 도우미를 사용할 수도 있습니다. 이는 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
}

사용되지 않는 필드

scopesexpires_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 레지스트리에 대한 서로 다른 수준의 액세스를 지원합니다. 이를 통해 사용자는 다음에 대한 인증을 할 수 없지만:

사용자는 컨테이너 레지스트리 API를 통해 레지스트리를 가져오고 나열하며 삭제할 수 있습니다.