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

OAuth 2.0์„ ํ™œ์šฉํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„: ๊ตฌ๊ธ€, ํŽ˜์ด์Šค๋ถ, ์นด์นด์˜ค ์—ฐ๋™ ๊ฐ€์ด๋“œ

mrmount 2024. 10. 19.

 

 

 

์†Œ์…œ ๋กœ๊ทธ์ธ์˜ ํ•„์š”์„ฑ๊ณผ OAuth 2.0์˜ ์—ญํ• 

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

 


 

1. ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ํ๋ฆ„

  1. ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ์š”์ฒญ : ์‚ฌ์šฉ์ž๊ฐ€ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  2. OAuth ์ œ๊ณต์ž์™€ ์ธ์ฆ ์—ฐ๋™ : ์‚ฌ์šฉ์ž๋Š” ์†Œ์…œ ์„œ๋น„์Šค์— ์ธ์ฆํ•˜๊ณ  ๊ถŒํ•œ์„ ๋ถ€์—ฌ ํ•ฉ๋‹ˆ๋‹ค.
  3. Access Token ๋ฐœ๊ธ‰ : ์ œ๊ณต์ž๋Š” Access Token์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  4. ์‚ฌ์šฉ์ž ์ •๋ณด ํ˜ธ์ถœ : Access Token์„ ์‚ฌ์šฉํ•ด API๋กœ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

 


 

2. ๊ตฌ๊ธ€ OAuth 2.0 ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ์˜ˆ์ œ

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

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

import requests

app = Flask(__name__)

app.secret_key = 'your_secret_key'

CLIENT_ID = 'your_google_client_id'

CLIENT_SECRET = 'your_google_client_secret'

REDIRECT_URI = 'http://localhost:5000/callback'

AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'

TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'

USER_INFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'

@app.route('/')

def home():

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

@app.route('/login')

def login():

    auth_url = f"{AUTH_URL}?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=email"

    return redirect(auth_url)

@app.route('/callback')

def callback():

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

    token_response = requests.post(TOKEN_URL, data={

        'code': code,

        'client_id': CLIENT_ID,

        'client_secret': CLIENT_SECRET,

        'redirect_uri': REDIRECT_URI,

        'grant_type': 'authorization_code'

    }).json()

    access_token = token_response['access_token']

    user_info = requests.get(USER_INFO_URL, headers={

        'Authorization': f'Bearer {access_token}'

    }).json()

    return f"Hello, {user_info['email']}!"

if __name__ == '__main__':

    app.run(debug=True)

 

 

์†Œ์…œ ๋กœ๊ทธ์ธ์˜ ํ•„์š”์„ฑ๊ณผ OAuth 2.0์˜ ์—ญํ• 

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

 


 

1. ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ํ๋ฆ„

  1. ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ์š”์ฒญ : ์‚ฌ์šฉ์ž๊ฐ€ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  2. OAuth ์ œ๊ณต์ž์™€ ์ธ์ฆ ์—ฐ๋™ : ์‚ฌ์šฉ์ž๋Š” ์†Œ์…œ ์„œ๋น„์Šค์— ์ธ์ฆํ•˜๊ณ  ๊ถŒํ•œ์„ ๋ถ€์—ฌ ํ•ฉ๋‹ˆ๋‹ค.
  3. Access Token ๋ฐœ๊ธ‰ : ์ œ๊ณต์ž๋Š” Access Token์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  4. ์‚ฌ์šฉ์ž ์ •๋ณด ํ˜ธ์ถœ : Access Token์„ ์‚ฌ์šฉํ•ด API๋กœ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

 


 

2. ๊ตฌ๊ธ€ OAuth 2.0 ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ์˜ˆ์ œ

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

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

import requests

app = Flask(__name__)

app.secret_key = 'your_secret_key'

CLIENT_ID = 'your_google_client_id'

CLIENT_SECRET = 'your_google_client_secret'

REDIRECT_URI = 'http://localhost:5000/callback'

AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'

TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'

USER_INFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'

@app.route('/')

def home():

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

@app.route('/login')

def login():

    auth_url = f"{AUTH_URL}?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=email"

    return redirect(auth_url)

@app.route('/callback')

def callback():

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

    token_response = requests.post(TOKEN_URL, data={

        'code': code,

        'client_id': CLIENT_ID,

        'client_secret': CLIENT_SECRET,

        'redirect_uri': REDIRECT_URI,

        'grant_type': 'authorization_code'

    }).json()

    access_token = token_response['access_token']

    user_info = requests.get(USER_INFO_URL, headers={

        'Authorization': f'Bearer {access_token}'

    }).json()

    return f"Hello, {user_info['email']}!"

if __name__ == '__main__':

    app.run(debug=True)

์„ค๋ช…: ์‚ฌ์šฉ์ž๋Š” ๊ตฌ๊ธ€ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ํ›„, Access Token ์„ ์‚ฌ์šฉํ•ด ์ด๋ฉ”์ผ ์ •๋ณด๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


 

3. Redirect URI ์„ค์ •๊ณผ ๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญ

 

Redirect URI ์„ค์ •

  • ์ •ํ™•ํ•œ URL : ์ œ๊ณต์ž ์ฝ˜์†”์— ๋“ฑ๋ก๋œ Redirect URI์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • HTTPS ์‚ฌ์šฉ : ๋ฏผ๊ฐํ•œ ์ •๋ณด๊ฐ€ ์˜ค๊ฐ€๋Š” ๊ฒฝ์šฐ HTTPS ๋ฅผ ์‚ฌ์šฉํ•ด ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
  • PKCE ์ ์šฉ : ๊ณต๊ฐœ ํด๋ผ์ด์–ธํŠธ(๋ชจ๋ฐ”์ผ ์•ฑ)์—์„œ๋Š” PKCE ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์‹œ: Redirect URI ๊ตฌ์„ฑ

https://example.com/callback

๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญ

  1. Access Token ๋ณดํ˜ธ : Access Token์€ ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. Scope ์ตœ์†Œํ™” : ์ตœ์†Œํ•œ์˜ ๊ถŒํ•œ๋งŒ ์š”์ฒญํ•ด ์‚ฌ์šฉ์ž ์‹ ๋ขฐ๋ฅผ ๋†’์ž…๋‹ˆ๋‹ค.
  3. ์„ธ์…˜ ํƒ€์ž„์•„์›ƒ : ๋กœ๊ทธ์ธ ์„ธ์…˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์„ค์ •ํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.

 


 

4. ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘๊ณผ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ

 

์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์š”์ฒญ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

  • ํ•„์ˆ˜ ์ •๋ณด๋งŒ ์š”์ฒญ : ์ด๋ฉ”์ผ, ์ด๋ฆ„ ๋“ฑ ์ตœ์†Œํ•œ์˜ ์ •๋ณด๋งŒ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  • ํˆฌ๋ช…ํ•œ ๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ : ์‚ฌ์šฉ์ž๊ฐ€ ๋™์˜ํ•œ ๋ฒ”์œ„ ๋‚ด์—์„œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ์ •์ฑ… ์ค€์ˆ˜ : ์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋Š” ๊ด€๋ จ ๋ฒ•๊ทœ ์— ๋”ฐ๋ผ ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 


 

5. ์†Œ์…œ ๋กœ๊ทธ์ธ ํŠธ๋ Œ๋“œ์™€ ํ™œ์šฉ ํ˜„ํ™ฉ

  • ์†Œ์…œ ๋กœ๊ทธ์ธ ์‚ฌ์šฉ ์ฆ๊ฐ€ : ํ•œ๊ตญ์—์„œ๋Š” ์นด์นด์˜ค์™€ ๋„ค์ด๋ฒ„ ์†Œ์…œ ๋กœ๊ทธ์ธ์ด ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฉ€ํ‹ฐ ๋กœ๊ทธ์ธ ์ง€์› : ๊ตฌ๊ธ€, ํŽ˜์ด์Šค๋ถ, ์นด์นด์˜ค ๋“ฑ ๋‹ค์–‘ํ•œ ์ œ๊ณต์ž๋ฅผ ํ†ตํ•ด ๋‹ค์ค‘ ๋กœ๊ทธ์ธ ํ™˜๊ฒฝ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ๊ฐ•ํ™” : GDPR ๋ฐ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ๋ฒ•์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘๊ณผ ์‚ฌ์šฉ์ด ๋”์šฑ ํˆฌ๋ช…ํ•ด์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 


 

FAQ

Q1. ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ์‹œ ์–ด๋–ค ์ ์— ์ฃผ์˜ํ•ด์•ผ ํ•˜๋‚˜์š”?
A1. Redirect URI์™€ ์Šค์ฝ”ํ”„ ์„ค์ • ์„ ์ •ํ™•ํžˆ ํ•˜๊ณ , Access Token์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q2. ์†Œ์…œ ๋กœ๊ทธ์ธ์—์„œ PKCE๋Š” ์–ธ์ œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋‚˜์š”?
A2. ๋ชจ๋ฐ”์ผ ์•ฑ๊ณผ ๊ฐ™์€ ๊ณต๊ฐœ ํด๋ผ์ด์–ธํŠธ ์—์„œ๋Š” PKCE๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q3. ์‚ฌ์šฉ์ž๊ฐ€ ๋™์˜ํ•œ ์ •๋ณด ์™ธ์— ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ๋‚˜์š”?
A3. ์‚ฌ์šฉ์ž ๋™์˜ ์—†์ด ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๊ฒƒ์€ ๊ธˆ์ง€ ๋˜๋ฉฐ, ๊ด€๋ จ ๋ฒ•๊ทœ๋ฅผ ์ค€์ˆ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

Q5. Access Token์€ ์–ผ๋งˆ๋‚˜ ์ž์ฃผ ๊ฐฑ์‹ ํ•ด์•ผ ํ•˜๋‚˜์š”?
A5. Access Token์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์งง์€ ์œ ํšจ ์‹œ๊ฐ„ ์„ ๊ฐ€์ง€๋ฉฐ, ๋งŒ๋ฃŒ ํ›„ Refresh Token ์„ ์‚ฌ์šฉํ•ด ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.

 


๋Œ“๊ธ€