--- description: v2023.9.0 이상에서 사용할 수 있는 OAuth2.0 방식의 인증 방법을 설명합니다. --- # OAuth 방식의 액세스 토큰 획득 방식 애플리케이션을 이용하는 사용자(이하 단순히 '사용자'라 칭함)의 액세스 토큰을 발급받으려면 아래 절차에 따라 발급을 요청합니다. :::tip 아래 설명할 방법은 [OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc6749.html)이라는 방법입니다.일반 OAuth는 앱을 만들어야 하지만, [IndieAuth](https://indieauth.spec.indieweb.org/)의 확장으로 앱을 만들지 않고도 사용할 수 있도록 되어 있습니다. OAuth 방식은 사용할 수 있는 라이브러리가 많기 때문에 가능하면 라이브러리를 사용하는 것을 추천합니다. 현재 이 방식을 사용하려면 웹페이지가 필요합니다.웹페이지를 준비할 수 없거나 Misskey 2023.9.0 이전 버전을 지원하고자 하는 경우, 아래의 방법을 사용하시기 바랍니다. - [Misskey 전용 MiAuth 방식으로 액세스 토큰을 얻는 방법](./oauth.md) - [앱 생성 방식으로 액세스 토큰을 획득하는 방법(구식).](./app.md) ::: ## Step 1 앱 소개를 위한 웹페이지를 만듭니다.페이지가 HTTPS 주소로 접속할 수 있어야 합니다.페이지 어딘가에 아래와 같은 HTML 코드를 작성합니다. ```html
``` 나중에 `redirect_uri` 주소로 인증 코드가 전송됩니다. ## Step 2 PKCE `code_verifier`와 `code_challenge` 문자열, 그리고 `state` 문자열을 생성합니다. - code_verifier`의 경우 최소 43자, 최대 128자로 알파벳 대/소문자 및 `-. _\~\` 안에 있는 문자로 제한됩니다. - code_challenge`문자열은`code_verifier\` 문자열을 SHA256 알고리즘으로 해시하여 base64url로 인코딩한 결과를 사용합니다. - `state` 문자열에는 특별한 제한이 없습니다. 임의의 문자열을 사용합니다. :::danger 이 문자열은 매번 생성해야 하며, 반복해서 사용하지 마십시오. ::: :::tip [pkce-challenge](https://www.npmjs.com/package/pkce-challenge)와 같은 라이브러리를 사용하거나 OAuth 라이브러리의 PKCE 기능을 사용하는 것을 추천합니다. ::: :::tip{label='例'} ```js import crypto from "node:crypto"; const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~"; const codeVerifier = new Array(128) .fill(0) .map(() => chars[Math.floor(chars.length * Math.random())]) .join(""); console.log('code_verifier', codeVerifier); const codeChallenge = crypto .createHash("sha256") .update(codeVerifier, "ascii") .digest("base64url"); console.log('code_challenge', codeChallenge); const state = crypto.randomUUID(); console.log('state', state); ``` ::: ## Step 3 상대 서버의 OAuth 정보를 가져옵니다. 데이터는 JSON 형식으로 되어 있습니다. ``` https://{host}/.well-known/oauth-authorization-server ``` `{host}`부분은 사용자 서버의 호스트로 대체합니다.보통 호스트는 사용자가 입력합니다. 여기서는 `authorization_endpoint`와 `token_endpoint`를 사용합니다. :::tip 다음 단계에서 사용되는 `scope`에 대한 정보도 `scopes_supported`에서 확인할 수 있습니다. ::: ## Step 4 애플리케이션 인증 양식을 사용자의 브라우저에 표시합니다.인증 양식은 다음 형식의 URL로 열 수 있습니다. ``` {authorization_endpoint}?client_id={client_id}&response_type=code&redirect_uri={redirect_uri}&scope={scope}&code_challenge={code_challenge}&code_challenge_method=S256&state={state} ``` 여기서, - `{authorization_endpoint}`부분은 이전 정보 획득에서 얻은 주소로 대체합니다. - `{client_id}`부분은 앱 소개 페이지의 주소로 대체합니다. - `{code_challenge}`부분은 앞서 생성한 `code_challenge` 문자열로 대체합니다. - `code_challenge_method`부분은 항상 `S256`으로 합니다. - `{redirect_uri}`부분은 소개 페이지에서 사용하고 있는 전송처 주소로 대체합니다. - `{scope}`부분은 애플리케이션이 요청하는 권한으로 대체합니다.요청하는 권한을 \`\`로 구분하여 열거합니다.권한 목록은 [여기](../permission.md)에서 확인할 수 있습니다. - `{state}`부분은 앞서 생성한 `state` 문자열로 대체합니다. :::tip{label='例'} ``` https://misskey.local/oauth/authorize?client_id=http%3A%2F%2Fexample.com&code_challenge=C6hwMO2bmIzg3nqppTE9b79fvuOjlrKmH2xNiZSMHzw&code_challenge_method=S256&response_type=code&redirect_uri=http%3A%2F%2Fexample.com%2Fredirect&scope=write%3Anotes&state=87c11f05-86eb-4eb2-9057-f6a98fc5e9ab ``` ::: ## Step 5 사용자가 애플리케이션 접근을 허용하면 `redirect_uri` 주소로 인증 코드가 URL 파라미터 형태로 전송됩니다. | 이름 | 설명 | | ------- | ----------------------- | | `code` | 사용자 인증 코드. | | `state` | 인증 요청에 사용된 `state` 문자열. | :::tip{label='例'} ``` https://example.com/redirect?code=...&state=87c11f05-86eb-4eb2-9057-f6a98fc5e9ab ``` ::: `state`문자열이 제대로 일치하는지 확인하고 다음 단계로 넘어갑니다. ## Step 6 전송된 인증 코드를 사용하여 POST로 액세스 토큰을 요청합니다.요청 대상은 `token_endpoint`가 됩니다.데이터 형식은 `application/json`과 `application/x-wwww-form-urlencoded`를 사용할 수 있습니다.각 매개변수는 다음과 같습니다. | 이름 | 설명 | | --------------- | ------------------------------- | | `grant_type` | 항상 `authorization_code`로 설정합니다. | | `client_id` | 인증 요청에 사용되는 `client_id` 문자열. | | `redirect_uri` | 인증 요청에 사용된 `redirect_uri` 문자열. | | `scope` | 인증 요청에 사용된 `scope` 문자열. | | `code` | 획득한 인증 코드. | | `code_verifier` | 앞서 생성한 `code_verifier` 문자열. | :::tip{label='例'} ```js const res = await fetch(endpoint, { method: "POST", body: JSON.stringify({ grant_type: "authorization_code", client_id: "https://example.com", redirect_uri: "https://example.com/redirect", scope: "write:notes", code: "...", code_verifier: "hjjbCYDmDpSLjirkO-PrfWKsRhDdJr-PAEGRClRwzUKlmFIIIrZNmSvUIraeIa~WqbqQnfbJV-Hc_IfuQkesBYUpukUi~lInDfU_AZjoZqbU.ioQTRzaFfZFfGnT-OAA", }), headers: { "Content-Type": "application/json" } }); ``` ::: 응답은 JSON 객체 형태로, 거기서 `access_token`을 가져와서 사용합니다.