mirror of
https://github.com/simple-login/app.git
synced 2024-11-10 21:27:10 +01:00
103 lines
3.4 KiB
Python
103 lines
3.4 KiB
Python
from flask import request, jsonify
|
|
from flask_cors import cross_origin
|
|
|
|
from app.extensions import db
|
|
from app.jose_utils import make_id_token
|
|
from app.log import LOG
|
|
from app.models import Client, AuthorizationCode, OauthToken, ClientUser
|
|
from app.oauth.base import oauth_bp
|
|
from app.oauth.views.authorize import generate_access_token
|
|
from app.oauth_models import Scope, get_response_types_from_str, ResponseType
|
|
|
|
|
|
@oauth_bp.route("/token", methods=["POST", "GET"])
|
|
@cross_origin()
|
|
def token():
|
|
"""
|
|
Calls by client to exchange the access token given the authorization code.
|
|
The client authentications using Basic Authentication.
|
|
The form contains the following data:
|
|
- grant_type: must be "authorization_code"
|
|
- code: the code obtained in previous step
|
|
"""
|
|
# Basic authentication
|
|
oauth_client_id = (
|
|
request.authorization and request.authorization.username
|
|
) or request.form.get("client_id")
|
|
|
|
oauth_client_secret = (
|
|
request.authorization and request.authorization.password
|
|
) or request.form.get("client_secret")
|
|
|
|
client = Client.filter_by(
|
|
oauth_client_id=oauth_client_id, oauth_client_secret=oauth_client_secret
|
|
).first()
|
|
|
|
if not client:
|
|
return jsonify(error="wrong client-id or client-secret"), 400
|
|
|
|
# Get code from form data
|
|
grant_type = request.form.get("grant_type")
|
|
code = request.form.get("code")
|
|
|
|
# sanity check
|
|
if grant_type != "authorization_code":
|
|
return jsonify(error="grant_type must be authorization_code"), 400
|
|
|
|
auth_code: AuthorizationCode = AuthorizationCode.filter_by(code=code).first()
|
|
if not auth_code:
|
|
return jsonify(error=f"no such authorization code {code}"), 400
|
|
elif auth_code.is_expired():
|
|
AuthorizationCode.delete(auth_code.id)
|
|
db.session.commit()
|
|
LOG.d("delete expired authorization code:%s", auth_code)
|
|
return jsonify(error=f"{code} already expired"), 400
|
|
|
|
if auth_code.client_id != client.id:
|
|
return jsonify(error=f"are you sure this code belongs to you?"), 400
|
|
|
|
LOG.debug(
|
|
"Create Oauth token for user %s, client %s", auth_code.user, auth_code.client
|
|
)
|
|
|
|
# Create token
|
|
oauth_token = OauthToken.create(
|
|
client_id=auth_code.client_id,
|
|
user_id=auth_code.user_id,
|
|
scope=auth_code.scope,
|
|
redirect_uri=auth_code.redirect_uri,
|
|
access_token=generate_access_token(),
|
|
response_type=auth_code.response_type,
|
|
)
|
|
db.session.add(oauth_token)
|
|
|
|
# Auth code can be used only once
|
|
AuthorizationCode.delete(auth_code.id)
|
|
|
|
db.session.commit()
|
|
|
|
client_user: ClientUser = ClientUser.get_by(
|
|
client_id=auth_code.client_id, user_id=auth_code.user_id
|
|
)
|
|
|
|
user_data = client_user.get_user_info()
|
|
|
|
res = {
|
|
"access_token": oauth_token.access_token,
|
|
"token_type": "Bearer",
|
|
"expires_in": 3600,
|
|
"scope": auth_code.scope,
|
|
"user": user_data, # todo: remove this
|
|
}
|
|
|
|
if oauth_token.scope and Scope.OPENID.value in oauth_token.scope:
|
|
res["id_token"] = make_id_token(client_user)
|
|
|
|
# Also return id_token if the initial flow is "code,id_token"
|
|
# cf https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660
|
|
response_types = get_response_types_from_str(auth_code.response_type)
|
|
if ResponseType.ID_TOKEN in response_types:
|
|
res["id_token"] = make_id_token(client_user)
|
|
|
|
return jsonify(res)
|