An OAuth server implements one or more of the following workflows:
1) Client Credential grant
2) Implicit grant
3) Authorization code grant
4) Refresh token grant
1) is for guest access without any user context
2) is less secure but supports Password patterns
3) is the typical OAuth workflow supporting anti-password pattern
4) is used to refresh the code/token after they expire
Simplest case is issue bearer token with indefinite expiration time by method 2) for user resource access and least secure.
For more details or for accuracy, please refer the RFC
For more details or for accuracy, please refer the RFC
# implicit workflow
# GET /oauth/authorize/v1?client_id=CarouselTest1&response_type=token&username=foo&password=bar HTTP/1.1
# HTTP/1.1 200 OK
# <meta http-equiv="refresh"content="0;url=http://127.0.0.1#expires_in=86399945&token_type=bearer&access_token=ACCESS_TOKEN">
# authorization workflow
# auth_code request
# GET /oauth/authorize/?client_id=BCTest1&redirect_uri=http%3A%2F%2F127.0.0.1:8888/callback&scope=openid HTTP/1.1
# Content-Type : application/x-www-form-urlencoded
# HTTP/1.1 302 Moved Temporarily
# Location: http://127.0.0.1/oauth/?code=AUTH_CODE
# access_token request
# POST /oauth/token/v1 HTTP/1.1
# Content-Type: application/x-www-form-urlencoded
# grant_type=authorization_code&client_id=THE_CLIENT_ID&client_secret=THE_CLIENT_SECRET&code=AUTH_CODE
# HTTP/1.1 200 OK
# {"token_type":"bearer","expires_in":86399945,"refresh_token":"REFRESH_TOKEN","access_token":"ACCESS_TOKEN"}
# refresh token request
# POST /oauth/token/v1 HTTP/1.1
# Content-Type: application/x-www-form-urlencoded
# grant_type=refresh_token&client_id=THE_CLIENT_ID&client_secret=THE_CLIENT_SECRET&refresh_token=REFRESH_TOKEN
# HTTP/1.1 200 OK
# {"token_type":"bearer","expires_in":86399968,"refresh_token":"REFRESH_TOKEN","access_token":"ACCESS_TOKEN"}
# client_credential request
# POST /oauth/token/v1 HTTP/1.1
# Content-Type: application/x-www-form-urlencoded
# grant_type=access_token&client_id=THE_CLIENT_ID&client_secret=THE_CLIENT_SECRET
# HTTP/1.1 200 OK
#{"token_type":"bearer","expires_in":86399945,"refresh_token":"REFRESH_TOKEN","access_token":"ACCESS_TOKEN"}
# coding: utf-8
from datetime import datetime, timedelta
from flask import Flask
from flask import session, request
from flask import make_response
from flask import render_template, redirect, jsonify
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import gen_salt
from flask_oauthlib.provider import OAuth2Provider
import random
import string
app = Flask(__name__, template_folder='templates')
app.debug = True
app.secret_key = 'notasecret'
app.config.update({
'SQLALCHEMY_DATABASE_URI': 'mysql://root:P@ssword@127.0.0.1/oauth',
})
db = SQLAlchemy(app)
#oauth = OAuth2Provider(app)
@app.route('/oauth/token/', methods=['POST'])
#@oauth.token_handler
def access_token():
client_id = request.args.get('client_id', '')
client_secret = request.args.get('client_secret', '')
if client_id.isspace() or client_secret.isspace(): # ideally we check registered clients
return make_response(jsonify({'error':'invalid client'}), 400)
grant_type = request.args.get('grant_type','')
if grant_type == 'authorization_code':
code = request.args.get('code', '') # ideally we check code issued
if code.isspace():
return jsonify({'error':'invalid code'}), 400
refresh_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
access_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
return jsonify({"token_type":"bearer","expires_in":3600,"refresh_token":refresh_token,"access_token":access_token}), 200
if grant_type == 'access_token':
# guest access token
refresh_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
access_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
return jsonify({"token_type":"bearer","expires_in":3600,"refresh_token":refresh_token,"access_token":access_token}), 200
if grant_type == 'refresh_token':
refresh_token = request.args.get('refresh_token', '')
if refresh_token.isspace():
return make_response(jsonify({'error':'invalid refresh_token'}, 400))
refresh_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
access_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
return jsonify({"token_type":"bearer","expires_in":3600,"refresh_token":refresh_token,"access_token":access_token}), 200
return make_response(jsonify({'error':'invalid grant_type'}), 400)
@app.route('/oauth/authorize/', methods=['GET'])
#@oauth.authorize_handler
def authorize(*args, **kwargs):
response_type = request.args.get('response_type', '')
if response_type == 'token':
# implicit
username = request.args.get('username', '')
password = request.args.get('password', '')
access_token = ''
if not username.isspace() and not password.isspace(): # ideally we check a membership provider
access_token = ''.join([random.choice('0123456789ABCDEF') for x in range(0, 16)]) # ideally we save tokens issued
return make_response(jsonify({'url':'http://127.0.0.1/', 'token_type': 'bearer', 'access_token': access_token, 'expires_in': 3600}), 200)
return make_response(jsonify({'error':'invalid credentials'}), 400)
else:
# auth code request
client_id = request.args.get('client_id', '') # ideally we check registered clients
if client_id.isspace():
return make_response(jsonify({'error':'invalid client'}), 400)
redirect_uri = request.args.get('redirect_uri', '') # ideally we check registered callback uri
if redirect_uri.isspace():
return make_response(jsonify({'error': 'invalid redirect_uri'}), 400)
auth_code = ''.join([random.choice(string.digits + string.ascii_uppercase) for x in range(0, 12)])
redirect_uri += "?code=" + auth_code # ideally we check for query strings
return redirect(redirect_uri)
if __name__ == '__main__':
app.run(port=8888)
Usually we don't send the client secret over the wire
Instead we could use something like this
# Here we simply propose the use of an OTP token to sign and make a request.
# implemented in totp.py discussed earlier as the OneTimePasswordAlgorithm ()
# for more detail, please visit : http://1drv.ms/13YBENz and code at https://github.com/ravibeta/apisecurity-1.0
otp = OneTimePasswordAlgorithm()
import hashlib
otp.generateTOTP(client_secret, epoch_time, '6', hashlib.sha256)
# Here we simply propose the use of an OTP token to sign and make a request.
# implemented in totp.py discussed earlier as the OneTimePasswordAlgorithm ()
# for more detail, please visit : http://1drv.ms/13YBENz and code at https://github.com/ravibeta/apisecurity-1.0
otp = OneTimePasswordAlgorithm()
import hashlib
otp.generateTOTP(client_secret, epoch_time, '6', hashlib.sha256)
'857652'
StringToSign = ?timestamp=23453231&token=857652
Put the HMAC_SHA1 of the above in the authorization header
StringToSign = ?timestamp=23453231&token=857652
Put the HMAC_SHA1 of the above in the authorization header
No comments:
Post a Comment