๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๊ตฌ๊ธ€ OAuth ์„ค์ •

mrmount 2024. 10. 19.

 

 

6. ๋‹ค์–‘ํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž์™€ ์—ฐ๋™ํ•˜๊ธฐ

์†Œ์…œ ๋กœ๊ทธ์ธ์„ ์ œ๊ณตํ•˜๋Š” ์ฃผ์š” ์ œ๊ณต์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฐ ํ”Œ๋žซํผ์€ OAuth 2.0 ํ‘œ์ค€ ์„ ๋”ฐ๋ฅด์ง€๋งŒ, ๊ตฌ์„ฑ๊ณผ ์‚ฌ์šฉ ๋ฐฉ๋ฒ• ์— ์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๊ตฌ๊ธ€ OAuth 2.0

    • ์‚ฌ์šฉ์ž ์ •๋ณด : ์ด๋ฉ”์ผ, ํ”„๋กœํ•„ ์‚ฌ์ง„, ์ด๋ฆ„ ๋“ฑ
    • ์‚ฌ์šฉ ์˜ˆ์‹œ : Google Drive ๋˜๋Š” Gmail API์™€ ํ†ตํ•ฉ

๊ตฌ๊ธ€ OAuth ๋ฌธ์„œ๐Ÿ‘†

2. ํŽ˜์ด์Šค๋ถ OAuth 2.0

    • ์‚ฌ์šฉ์ž ์ •๋ณด : ํ”„๋กœํ•„ ์ •๋ณด, ์นœ๊ตฌ ๋ชฉ๋ก, ํŽ˜์ด์ง€ ์ •๋ณด ๋“ฑ
    • ์‚ฌ์šฉ ์˜ˆ์‹œ : ํŽ˜์ด์Šค๋ถ ํŽ˜์ด์ง€ ๊ด€๋ฆฌ ๋˜๋Š” ๋งˆ์ผ€ํŒ… ๋„๊ตฌ ์—ฐ๋™

ํŽ˜์ด์Šค๋ถ ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๐Ÿ‘†

3. ์นด์นด์˜ค OAuth 2.0

    • ์‚ฌ์šฉ์ž ์ •๋ณด : ์นด์นด์˜คํ†ก ํ”„๋กœํ•„, ์ด๋ฉ”์ผ, ์นœ๊ตฌ ๋ชฉ๋ก ๋“ฑ
    • ์‚ฌ์šฉ ์˜ˆ์‹œ : ์นด์นด์˜คํ†ก ๋กœ๊ทธ์ธ๊ณผ ๋ฉ”์‹œ์ง€ API ํ™œ์šฉ

์นด์นด์˜ค ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๐Ÿ‘†

 


 

7. ๋‹ค์ค‘ ์ œ๊ณต์ž ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ์˜ˆ์ œ

์•„๋ž˜๋Š” Flask ๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌ๊ธ€๊ณผ ์นด์นด์˜ค์˜ OAuth ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ์„ ๋™์‹œ์— ์ œ๊ณตํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

์˜ˆ์ œ ์ฝ”๋“œ: Flask์™€ ๋‹ค์ค‘ ์†Œ์…œ ๋กœ๊ทธ์ธ

from flask import Flask, redirect, request, session, url_for

import requests

app = Flask(__name__)

app.secret_key = 'your_secret_key'

# ๊ตฌ๊ธ€ OAuth ์„ค์ •

GOOGLE_CLIENT_ID = 'your_google_client_id'

GOOGLE_CLIENT_SECRET = 'your_google_client_secret'

GOOGLE_REDIRECT_URI = 'http://localhost:5000/google/callback'

# ์นด์นด์˜ค OAuth ์„ค์ •

KAKAO_CLIENT_ID = 'your_kakao_client_id'

KAKAO_REDIRECT_URI = 'http://localhost:5000/kakao/callback'

@app.route('/')

def home():

    return '''

    <a href="/login/google">๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ</a><br>

    <a href="/login/kakao">์นด์นด์˜ค ๋กœ๊ทธ์ธ</a>

    '''

@app.route('/login/google')

def login_google():

    google_auth_url = (

        f"https://accounts.google.com/o/oauth2/auth?response_type=code"

        f"&client_id={GOOGLE_CLIENT_ID}&redirect_uri={GOOGLE_REDIRECT_URI}&scope=email"

    )

    return redirect(google_auth_url)

@app.route('/google/callback')

def google_callback():

    code = request.args.get('code')

    token_response = requests.post('https://oauth2.googleapis.com/token', data={

        'code': code,

        'client_id': GOOGLE_CLIENT_ID,

        'client_secret': GOOGLE_CLIENT_SECRET,

        'redirect_uri': GOOGLE_REDIRECT_URI,

        'grant_type': 'authorization_code'

    }).json()

    access_token = token_response['access_token']

    user_info = requests.get('https://www.googleapis.com/oauth2/v1/userinfo', headers={

        'Authorization': f'Bearer {access_token}'

    }).json()

    return f"๊ตฌ๊ธ€ ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ: {user_info['email']}"

@app.route('/login/kakao')

def login_kakao():

    kakao_auth_url = (

        f"https://kauth.kakao.com/oauth/authorize?response_type=code"

        f"&client_id={KAKAO_CLIENT_ID}&redirect_uri={KAKAO_REDIRECT_URI}"

    )

    return redirect(kakao_auth_url)

@app.route('/kakao/callback')

def kakao_callback():

    code = request.args.get('code')

    token_response = requests.post('https://kauth.kakao.com/oauth/token', data={

        'grant_type': 'authorization_code',

        'client_id': KAKAO_CLIENT_ID,

        'redirect_uri': KAKAO_REDIRECT_URI,

        'code': code

    }).json()

    access_token = token_response['access_token']

    user_info = requests.get('https://kapi.kakao.com/v2/user/me', headers={

        'Authorization': f'Bearer {access_token}'

    }).json()

    return f"์นด์นด์˜ค ์‚ฌ์šฉ์ž ID: {user_info['id']}"

if __name__ == '__main__':

    app.run(debug=True)

 


 

6. ๋‹ค์–‘ํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž์™€ ์—ฐ๋™ํ•˜๊ธฐ

์†Œ์…œ ๋กœ๊ทธ์ธ์„ ์ œ๊ณตํ•˜๋Š” ์ฃผ์š” ์ œ๊ณต์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฐ ํ”Œ๋žซํผ์€ OAuth 2.0 ํ‘œ์ค€ ์„ ๋”ฐ๋ฅด์ง€๋งŒ, ๊ตฌ์„ฑ๊ณผ ์‚ฌ์šฉ ๋ฐฉ๋ฒ• ์— ์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๊ตฌ๊ธ€ OAuth 2.0

    • ์‚ฌ์šฉ์ž ์ •๋ณด : ์ด๋ฉ”์ผ, ํ”„๋กœํ•„ ์‚ฌ์ง„, ์ด๋ฆ„ ๋“ฑ
    • ์‚ฌ์šฉ ์˜ˆ์‹œ : Google Drive ๋˜๋Š” Gmail API์™€ ํ†ตํ•ฉ

๊ตฌ๊ธ€ OAuth ๋ฌธ์„œ๐Ÿ‘†

2. ํŽ˜์ด์Šค๋ถ OAuth 2.0

    • ์‚ฌ์šฉ์ž ์ •๋ณด : ํ”„๋กœํ•„ ์ •๋ณด, ์นœ๊ตฌ ๋ชฉ๋ก, ํŽ˜์ด์ง€ ์ •๋ณด ๋“ฑ
    • ์‚ฌ์šฉ ์˜ˆ์‹œ : ํŽ˜์ด์Šค๋ถ ํŽ˜์ด์ง€ ๊ด€๋ฆฌ ๋˜๋Š” ๋งˆ์ผ€ํŒ… ๋„๊ตฌ ์—ฐ๋™

ํŽ˜์ด์Šค๋ถ ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๐Ÿ‘†

3. ์นด์นด์˜ค OAuth 2.0

    • ์‚ฌ์šฉ์ž ์ •๋ณด : ์นด์นด์˜คํ†ก ํ”„๋กœํ•„, ์ด๋ฉ”์ผ, ์นœ๊ตฌ ๋ชฉ๋ก ๋“ฑ
    • ์‚ฌ์šฉ ์˜ˆ์‹œ : ์นด์นด์˜คํ†ก ๋กœ๊ทธ์ธ๊ณผ ๋ฉ”์‹œ์ง€ API ํ™œ์šฉ

์นด์นด์˜ค ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๐Ÿ‘†

 


 

7. ๋‹ค์ค‘ ์ œ๊ณต์ž ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ์˜ˆ์ œ

์•„๋ž˜๋Š” Flask ๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌ๊ธ€๊ณผ ์นด์นด์˜ค์˜ OAuth ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ์„ ๋™์‹œ์— ์ œ๊ณตํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

์˜ˆ์ œ ์ฝ”๋“œ: Flask์™€ ๋‹ค์ค‘ ์†Œ์…œ ๋กœ๊ทธ์ธ

from flask import Flask, redirect, request, session, url_for

import requests

app = Flask(__name__)

app.secret_key = 'your_secret_key'

# ๊ตฌ๊ธ€ OAuth ์„ค์ •

GOOGLE_CLIENT_ID = 'your_google_client_id'

GOOGLE_CLIENT_SECRET = 'your_google_client_secret'

GOOGLE_REDIRECT_URI = 'http://localhost:5000/google/callback'

# ์นด์นด์˜ค OAuth ์„ค์ •

KAKAO_CLIENT_ID = 'your_kakao_client_id'

KAKAO_REDIRECT_URI = 'http://localhost:5000/kakao/callback'

@app.route('/')

def home():

    return '''

    <a href="/login/google">๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ</a><br>

    <a href="/login/kakao">์นด์นด์˜ค ๋กœ๊ทธ์ธ</a>

    '''

@app.route('/login/google')

def login_google():

    google_auth_url = (

        f"https://accounts.google.com/o/oauth2/auth?response_type=code"

        f"&client_id={GOOGLE_CLIENT_ID}&redirect_uri={GOOGLE_REDIRECT_URI}&scope=email"

    )

    return redirect(google_auth_url)

@app.route('/google/callback')

def google_callback():

    code = request.args.get('code')

    token_response = requests.post('https://oauth2.googleapis.com/token', data={

        'code': code,

        'client_id': GOOGLE_CLIENT_ID,

        'client_secret': GOOGLE_CLIENT_SECRET,

        'redirect_uri': GOOGLE_REDIRECT_URI,

        'grant_type': 'authorization_code'

    }).json()

    access_token = token_response['access_token']

    user_info = requests.get('https://www.googleapis.com/oauth2/v1/userinfo', headers={

        'Authorization': f'Bearer {access_token}'

    }).json()

    return f"๊ตฌ๊ธ€ ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ: {user_info['email']}"

@app.route('/login/kakao')

def login_kakao():

    kakao_auth_url = (

        f"https://kauth.kakao.com/oauth/authorize?response_type=code"

        f"&client_id={KAKAO_CLIENT_ID}&redirect_uri={KAKAO_REDIRECT_URI}"

    )

    return redirect(kakao_auth_url)

@app.route('/kakao/callback')

def kakao_callback():

    code = request.args.get('code')

    token_response = requests.post('https://kauth.kakao.com/oauth/token', data={

        'grant_type': 'authorization_code',

        'client_id': KAKAO_CLIENT_ID,

        'redirect_uri': KAKAO_REDIRECT_URI,

        'code': code

    }).json()

    access_token = token_response['access_token']

    user_info = requests.get('https://kapi.kakao.com/v2/user/me', headers={

        'Authorization': f'Bearer {access_token}'

    }).json()

    return f"์นด์นด์˜ค ์‚ฌ์šฉ์ž ID: {user_info['id']}"

if __name__ == '__main__':

    app.run(debug=True)

์ฝ”๋“œ ์„ค๋ช…

  • ์‚ฌ์šฉ์ž๋Š” ๊ตฌ๊ธ€ ๋˜๋Š” ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  • ๋กœ๊ทธ์ธ ํ›„ Access Token ์„ ๋ฐœ๊ธ‰๋ฐ›์•„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค์ค‘ ์ œ๊ณต์ž ๋กœ๊ทธ์ธ์„ ์ง€์›ํ•˜๋ฉฐ, ์ถ”๊ฐ€ ์ œ๊ณต์ž ๋„ ์†์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


 

8. ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ์‹œ์˜ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

 

๋ฌธ์ œ 1: Redirect URI ๋ถˆ์ผ์น˜ ์˜ค๋ฅ˜

  • ๋ฌธ์ œ: ์ œ๊ณต์ž ์ฝ˜์†”์— ๋“ฑ๋ก๋œ Redirect URI์™€ ์‹ค์ œ ์š”์ฒญ URI๊ฐ€ ๋‹ค๋ฅด๋ฉด ์ธ์ฆ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ: ๋ชจ๋“  Redirect URI๊ฐ€ ์ •ํ™•ํžˆ ์ผ์น˜ ํ•˜๋„๋ก ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ 2: ๊ถŒํ•œ ์š”์ฒญ ์‹คํŒจ

  • ๋ฌธ์ œ: ํ•„์ˆ˜ ๊ถŒํ•œ์„ ์š”์ฒญํ•˜์ง€ ๋ชปํ•˜๋ฉด API ํ˜ธ์ถœ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ: ํ•„์š”ํ•œ ์Šค์ฝ”ํ”„๋ฅผ ์ •ํ™•ํžˆ ๋ช…์‹œ ํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ 3: ๋งŒ๋ฃŒ๋œ Access Token

  • ๋ฌธ์ œ: Access Token์ด ๋งŒ๋ฃŒ๋˜๋ฉด API ํ˜ธ์ถœ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ: Refresh Token ์„ ์‚ฌ์šฉํ•ด ์ž๋™์œผ๋กœ ์ƒˆ๋กœ์šด Access Token์„ ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

 


 

๊ฒฐ๋ก : OAuth 2.0์„ ํ™œ์šฉํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌ์ถ•์˜ ํ•ต์‹ฌ

OAuth 2.0์„ ํ™œ์šฉํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„  ํ•˜๊ณ  ๊ณ„์ • ๊ด€๋ฆฌ ๋ถ€๋‹ด์„ ์ค„์ด๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์ œ๊ณต์ž์™€ ์—ฐ๋™ํ•˜์—ฌ ๊ตฌ๊ธ€, ํŽ˜์ด์Šค๋ถ, ์นด์นด์˜ค ์™€ ๊ฐ™์€ ํ”Œ๋žซํผ์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, Redirect URI์™€ ์Šค์ฝ”ํ”„ ์„ค์ • ์„ ์ •ํ™•ํžˆ ํ•˜๊ณ  ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•œ PKCE ์‚ฌ์šฉ ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 


 

FAQ

Q1. ์†Œ์…œ ๋กœ๊ทธ์ธ ์‹œ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?
A1. PKCE ์‚ฌ์šฉ ๊ณผ HTTPS ์ „์†ก ์œผ๋กœ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q2. ์‚ฌ์šฉ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ๋กœ๊ทธ์ธํ•ด์•ผ ํ•˜๋Š” ๋ถˆํŽธํ•จ์„ ํ•ด๊ฒฐํ•  ๋ฐฉ๋ฒ•์ด ์žˆ๋‚˜์š”?
A2. Refresh Token ์„ ์‚ฌ์šฉํ•ด Access Token์„ ์ž๋™์œผ๋กœ ๊ฐฑ์‹ ํ•ด ๋กœ๊ทธ์ธ ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q3. ์†Œ์…œ ๋กœ๊ทธ์ธ์— ํ•„์š”ํ•œ ์Šค์ฝ”ํ”„๋Š” ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•˜๋‚˜์š”?
A3. ๊ฐ ์ œ๊ณต์ž์˜ ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์—์„œ ํ•„์š”ํ•œ ๊ถŒํ•œ(์Šค์ฝ”ํ”„) ์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q4. ๋‹ค์ค‘ ์ œ๊ณต์ž ๋กœ๊ทธ์ธ์„ ๋™์‹œ์— ์ง€์›ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?
A4. ๊ฐ ์ œ๊ณต์ž์˜ OAuth ์„ค์ •์„ ๋”ฐ๋กœ ๊ตฌ์„ฑํ•ด ๊ตฌ๊ธ€, ์นด์นด์˜ค, ํŽ˜์ด์Šค๋ถ ๋กœ๊ทธ์ธ์„ ๋ชจ๋‘ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q5. ์†Œ์…œ ๋กœ๊ทธ์ธ ์‹œ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?
A5. ์ตœ์†Œํ•œ์˜ ์‚ฌ์šฉ์ž ์ •๋ณด ๋งŒ ์š”์ฒญํ•˜๊ณ , ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ์ •์ฑ…์„ ๋ช…ํ™•ํžˆ ๊ณ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 


๋Œ“๊ธ€