forked from Github-Mirrors/canaille
Some authorization_code work
This commit is contained in:
parent
d75fcb163b
commit
ea98ca6702
10 changed files with 200 additions and 94 deletions
|
@ -25,6 +25,7 @@ olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.3 NAME 'oauthRedirectURI'
|
||||||
ORDERING caseIgnoreOrderingMatch
|
ORDERING caseIgnoreOrderingMatch
|
||||||
SUBSTR caseIgnoreSubstringsMatch
|
SUBSTR caseIgnoreSubstringsMatch
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||||
|
SINGLE-VALUE
|
||||||
USAGE userApplications
|
USAGE userApplications
|
||||||
X-ORIGIN 'OAuth 2.0' )
|
X-ORIGIN 'OAuth 2.0' )
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.4 NAME 'oauthResponseType'
|
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.4 NAME 'oauthResponseType'
|
||||||
|
@ -49,6 +50,7 @@ olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.6 NAME 'oauthNonce'
|
||||||
ORDERING caseExactOrderingMatch
|
ORDERING caseExactOrderingMatch
|
||||||
SUBSTR caseExactSubstringsMatch
|
SUBSTR caseExactSubstringsMatch
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||||
|
SINGLE-VALUE
|
||||||
USAGE userApplications
|
USAGE userApplications
|
||||||
X-ORIGIN 'OAuth 2.0' )
|
X-ORIGIN 'OAuth 2.0' )
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.7 NAME 'oauthAuthorizationDate'
|
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.7 NAME 'oauthAuthorizationDate'
|
||||||
|
@ -251,6 +253,31 @@ olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.29 NAME 'oauthTokenEndpointAuthMethod
|
||||||
SINGLE-VALUE
|
SINGLE-VALUE
|
||||||
USAGE userApplications
|
USAGE userApplications
|
||||||
X-ORIGIN 'OAuth 2.0 Dynamic Client Registration Protocol' )
|
X-ORIGIN 'OAuth 2.0 Dynamic Client Registration Protocol' )
|
||||||
|
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.30 NAME 'oauthSubject'
|
||||||
|
DESC 'OAuth 2.0 Token subject'
|
||||||
|
EQUALITY caseExactMatch
|
||||||
|
ORDERING caseExactOrderingMatch
|
||||||
|
SUBSTR caseExactSubstringsMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||||
|
SINGLE-VALUE
|
||||||
|
USAGE userApplications
|
||||||
|
X-ORIGIN 'OAuth 2.0 Dynamic Client Registration Protocol' )
|
||||||
|
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.31 NAME 'oauthRedirectURIs'
|
||||||
|
DESC 'Authorization Code Redirection URI'
|
||||||
|
EQUALITY caseIgnoreMatch
|
||||||
|
ORDERING caseIgnoreOrderingMatch
|
||||||
|
SUBSTR caseIgnoreSubstringsMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||||
|
USAGE userApplications
|
||||||
|
X-ORIGIN 'OAuth 2.0' )
|
||||||
|
olcAttributeTypes: ( 1.3.6.1.4.1.56207.1.1.32 NAME 'oauthAuthorizationLifetime'
|
||||||
|
DESC 'OAuth 2.0 authorization code lifetime, in seconds'
|
||||||
|
EQUALITY integerMatch
|
||||||
|
ORDERING integerOrderingMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
|
||||||
|
SINGLE-VALUE
|
||||||
|
USAGE userApplications
|
||||||
|
X-ORIGIN 'OAuth 2.0' )
|
||||||
olcObjectClasses: ( 1.3.6.1.4.1.56207.1.2.1 NAME 'oauthClient'
|
olcObjectClasses: ( 1.3.6.1.4.1.56207.1.2.1 NAME 'oauthClient'
|
||||||
DESC 'OAuth 2.0 Authorization Code'
|
DESC 'OAuth 2.0 Authorization Code'
|
||||||
SUP top
|
SUP top
|
||||||
|
@ -260,7 +287,7 @@ olcObjectClasses: ( 1.3.6.1.4.1.56207.1.2.1 NAME 'oauthClient'
|
||||||
oauthClientName $
|
oauthClientName $
|
||||||
oauthClientContact $
|
oauthClientContact $
|
||||||
oauthClientURI $
|
oauthClientURI $
|
||||||
oauthRedirectURI $
|
oauthRedirectURIs $
|
||||||
oauthLogoURI $
|
oauthLogoURI $
|
||||||
oauthIssueDate $
|
oauthIssueDate $
|
||||||
oauthClientSecret $
|
oauthClientSecret $
|
||||||
|
@ -283,13 +310,14 @@ olcObjectClasses: ( 1.3.6.1.4.1.56207.1.2.2 NAME 'oauthAuthorizationCode'
|
||||||
STRUCTURAL
|
STRUCTURAL
|
||||||
MUST oauthCode
|
MUST oauthCode
|
||||||
MAY ( description $
|
MAY ( description $
|
||||||
oauthCode $
|
|
||||||
oauthClientID $
|
oauthClientID $
|
||||||
|
oauthSubject $
|
||||||
oauthRedirectURI $
|
oauthRedirectURI $
|
||||||
oauthResponseType $
|
oauthResponseType $
|
||||||
oauthScope $
|
oauthScope $
|
||||||
oauthNonce $
|
oauthNonce $
|
||||||
oauthAuthorizationDate $
|
oauthAuthorizationDate $
|
||||||
|
oauthAuthorizationLifetime $
|
||||||
oauthCodeChallenge $
|
oauthCodeChallenge $
|
||||||
oauthCodeChallengeMethod )
|
oauthCodeChallengeMethod )
|
||||||
X-ORIGIN 'OAuth 2.0' )
|
X-ORIGIN 'OAuth 2.0' )
|
||||||
|
@ -300,6 +328,7 @@ olcObjectClasses: ( 1.3.6.1.4.1.56207.1.2.3 NAME 'oauthToken'
|
||||||
MUST oauthAccessToken
|
MUST oauthAccessToken
|
||||||
MAY ( description $
|
MAY ( description $
|
||||||
oauthClientID $
|
oauthClientID $
|
||||||
|
oauthSubject $
|
||||||
oauthTokenType $
|
oauthTokenType $
|
||||||
oauthRefreshToken $
|
oauthRefreshToken $
|
||||||
oauthScope $
|
oauthScope $
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import ldap
|
import ldap
|
||||||
import os
|
import os
|
||||||
import toml
|
import toml
|
||||||
from . import routes, clients
|
from . import routes, clients, oauth
|
||||||
|
|
||||||
from flask import Flask, g, request
|
from flask import Flask, g, request
|
||||||
from flask_babel import Babel
|
from flask_babel import Babel
|
||||||
|
@ -39,6 +39,7 @@ def setup_app(app):
|
||||||
|
|
||||||
config_oauth(app)
|
config_oauth(app)
|
||||||
app.register_blueprint(routes.bp)
|
app.register_blueprint(routes.bp)
|
||||||
|
app.register_blueprint(oauth.bp, url_prefix="/oauth")
|
||||||
app.register_blueprint(clients.bp, url_prefix="/client")
|
app.register_blueprint(clients.bp, url_prefix="/client")
|
||||||
|
|
||||||
babel = Babel(app)
|
babel = Babel(app)
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ClientAdd(FlaskForm):
|
||||||
validators=[wtforms.validators.DataRequired()],
|
validators=[wtforms.validators.DataRequired()],
|
||||||
render_kw={"placeholder": "https://mydomain.tld"},
|
render_kw={"placeholder": "https://mydomain.tld"},
|
||||||
)
|
)
|
||||||
oauthRedirectURI = wtforms.fields.html5.URLField(
|
oauthRedirectURIs = wtforms.fields.html5.URLField(
|
||||||
gettext("Redirect URIs"),
|
gettext("Redirect URIs"),
|
||||||
validators=[wtforms.validators.DataRequired()],
|
validators=[wtforms.validators.DataRequired()],
|
||||||
render_kw={"placeholder": "https://mydomain.tld/callback"},
|
render_kw={"placeholder": "https://mydomain.tld/callback"},
|
||||||
|
@ -129,7 +129,7 @@ def add():
|
||||||
oauthClientContact=form["oauthClientContact"].data,
|
oauthClientContact=form["oauthClientContact"].data,
|
||||||
oauthClientURI=form["oauthClientURI"].data,
|
oauthClientURI=form["oauthClientURI"].data,
|
||||||
oauthGrantType=form["oauthGrantType"].data,
|
oauthGrantType=form["oauthGrantType"].data,
|
||||||
oauthRedirectURI=[form["oauthRedirectURI"].data],
|
oauthRedirectURIs=[form["oauthRedirectURIs"].data],
|
||||||
oauthResponseType=form["oauthResponseType"].data,
|
oauthResponseType=form["oauthResponseType"].data,
|
||||||
oauthScope=form["oauthScope"].data.split(" "),
|
oauthScope=form["oauthScope"].data.split(" "),
|
||||||
oauthTokenEndpointAuthMethod=form["oauthTokenEndpointAuthMethod"].data,
|
oauthTokenEndpointAuthMethod=form["oauthTokenEndpointAuthMethod"].data,
|
||||||
|
@ -157,7 +157,7 @@ def edit(client_id):
|
||||||
client = Client.get(client_id)
|
client = Client.get(client_id)
|
||||||
data = dict(client)
|
data = dict(client)
|
||||||
data["oauthScope"] = " ".join(data["oauthScope"])
|
data["oauthScope"] = " ".join(data["oauthScope"])
|
||||||
data["oauthRedirectURI"] = data["oauthRedirectURI"][0]
|
data["oauthRedirectURIs"] = data["oauthRedirectURIs"][0]
|
||||||
form = ClientAdd(request.form or None, data=data, client=client)
|
form = ClientAdd(request.form or None, data=data, client=client)
|
||||||
|
|
||||||
if not request.form:
|
if not request.form:
|
||||||
|
@ -175,7 +175,7 @@ def edit(client_id):
|
||||||
oauthClientContact=form["oauthClientContact"].data,
|
oauthClientContact=form["oauthClientContact"].data,
|
||||||
oauthClientURI=form["oauthClientURI"].data,
|
oauthClientURI=form["oauthClientURI"].data,
|
||||||
oauthGrantType=form["oauthGrantType"].data,
|
oauthGrantType=form["oauthGrantType"].data,
|
||||||
oauthRedirectURI=[form["oauthRedirectURI"].data],
|
oauthRedirectURIs=[form["oauthRedirectURIs"].data],
|
||||||
oauthResponseType=form["oauthResponseType"].data,
|
oauthResponseType=form["oauthResponseType"].data,
|
||||||
oauthScope=form["oauthScope"].data.split(" "),
|
oauthScope=form["oauthScope"].data.split(" "),
|
||||||
oauthTokenEndpointAuthMethod=form["oauthTokenEndpointAuthMethod"].data,
|
oauthTokenEndpointAuthMethod=form["oauthTokenEndpointAuthMethod"].data,
|
||||||
|
|
|
@ -24,6 +24,13 @@ class LDAPObjectHelper:
|
||||||
self.may.extend(oc.may)
|
self.may.extend(oc.may)
|
||||||
self.must.extend(oc.must)
|
self.must.extend(oc.must)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<{} {}={}>".format(
|
||||||
|
self.__class__.__name__,
|
||||||
|
self.id,
|
||||||
|
getattr(self, self.id)
|
||||||
|
)
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
return self.must + self.may
|
return self.must + self.may
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,13 @@ class User(LDAPObjectHelper):
|
||||||
base = "ou=users,dc=mydomain,dc=tld"
|
base = "ou=users,dc=mydomain,dc=tld"
|
||||||
id = "cn"
|
id = "cn"
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.cn[0]
|
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
return password == "valid"
|
return password == "valid"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.cn[0]
|
||||||
|
|
||||||
|
|
||||||
class Client(LDAPObjectHelper, ClientMixin):
|
class Client(LDAPObjectHelper, ClientMixin):
|
||||||
objectClass = ["oauthClient"]
|
objectClass = ["oauthClient"]
|
||||||
|
@ -34,13 +35,13 @@ class Client(LDAPObjectHelper, ClientMixin):
|
||||||
return self.oauthClientID
|
return self.oauthClientID
|
||||||
|
|
||||||
def get_default_redirect_uri(self):
|
def get_default_redirect_uri(self):
|
||||||
return self.oauthRedirectURI
|
return self.oauthRedirectURIs[0]
|
||||||
|
|
||||||
def get_allowed_scope(self, scope):
|
def get_allowed_scope(self, scope):
|
||||||
return self.oauthScope
|
return self.oauthScope
|
||||||
|
|
||||||
def check_redirect_uri(self, redirect_uri):
|
def check_redirect_uri(self, redirect_uri):
|
||||||
return redirect_uri in self.oauthRedirectURI
|
return redirect_uri in self.oauthRedirectURIs
|
||||||
|
|
||||||
def has_client_secret(self):
|
def has_client_secret(self):
|
||||||
return bool(self.oauthClientSecret)
|
return bool(self.oauthClientSecret)
|
||||||
|
@ -96,7 +97,7 @@ class AuthorizationCode(LDAPObjectHelper, AuthorizationCodeMixin):
|
||||||
id = "oauthCode"
|
id = "oauthCode"
|
||||||
|
|
||||||
def get_redirect_uri(self):
|
def get_redirect_uri(self):
|
||||||
return Client.get(self.authzClientID).oauthRedirectURI
|
return self.oauthRedirectURI
|
||||||
|
|
||||||
def get_scope(self):
|
def get_scope(self):
|
||||||
return self.oauth2ScopeValue
|
return self.oauth2ScopeValue
|
||||||
|
@ -108,13 +109,13 @@ class AuthorizationCode(LDAPObjectHelper, AuthorizationCodeMixin):
|
||||||
return expires_at >= time.time()
|
return expires_at >= time.time()
|
||||||
|
|
||||||
def get_client_id(self):
|
def get_client_id(self):
|
||||||
return self.client_id
|
return self.oauthClientID
|
||||||
|
|
||||||
def get_expires_in(self):
|
def get_expires_in(self):
|
||||||
return self.expires_in
|
return self.oauthAuthorizationLifetime
|
||||||
|
|
||||||
def get_expires_at(self):
|
def get_expires_at(self):
|
||||||
return self.issued_at + self.expires_in
|
return datetime.datetime.strptime(self.oauthAuthorizationDate, "%Y%m%d%H%M%SZ") + datetime.timedelta(seconds=int(self.oauthAuthorizationLifetime))
|
||||||
|
|
||||||
|
|
||||||
class Token(LDAPObjectHelper, TokenMixin):
|
class Token(LDAPObjectHelper, TokenMixin):
|
||||||
|
|
71
web/oauth.py
Normal file
71
web/oauth.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import wtforms
|
||||||
|
from authlib.oauth2 import OAuth2Error
|
||||||
|
from flask import Blueprint, request, session, redirect
|
||||||
|
from flask import render_template, jsonify, flash
|
||||||
|
from flask_babel import gettext
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from .models import User, Client
|
||||||
|
from .oauth2utils import authorization
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint(__name__, "oauth")
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
login = wtforms.StringField(
|
||||||
|
gettext("Username"),
|
||||||
|
validators=[wtforms.validators.DataRequired()],
|
||||||
|
render_kw={"placeholder": "mdupont"},
|
||||||
|
)
|
||||||
|
password = wtforms.PasswordField(
|
||||||
|
gettext("Password"), validators=[wtforms.validators.DataRequired()]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/authorize", methods=["GET", "POST"])
|
||||||
|
def authorize():
|
||||||
|
user = User.get(session["user_dn"]) if "user_dn" in session else None
|
||||||
|
client = Client.get(request.values["client_id"])
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
form = LoginForm(request.form or None)
|
||||||
|
if request.method == "GET":
|
||||||
|
return render_template("login.html", form=form)
|
||||||
|
|
||||||
|
if not form.validate():
|
||||||
|
flash(gettext("Login failed, please check your information"), "error")
|
||||||
|
return render_template("login.html", form=form)
|
||||||
|
|
||||||
|
user = User.get(form.login.data)
|
||||||
|
if not user or not user.check_password(form.password.data):
|
||||||
|
flash(gettext("Login failed, please check your information"), "error")
|
||||||
|
return render_template("login.html", form=form)
|
||||||
|
|
||||||
|
session["user_dn"] = form.login.data
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
try:
|
||||||
|
grant = authorization.validate_consent_request(end_user=user)
|
||||||
|
except OAuth2Error as error:
|
||||||
|
return jsonify(dict(error.get_body()))
|
||||||
|
|
||||||
|
return render_template("authorize.html", user=user, grant=grant, client=client)
|
||||||
|
|
||||||
|
if request.form["answer"] == "logout":
|
||||||
|
del session["user_dn"]
|
||||||
|
flash(gettext("You have been successfully logged out."), "success")
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
|
if request.form["answer"] == "deny":
|
||||||
|
grant_user = None
|
||||||
|
|
||||||
|
if request.form["answer"] == "accept":
|
||||||
|
grant_user = user.dn
|
||||||
|
|
||||||
|
return authorization.create_authorization_response(grant_user=grant_user)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/token", methods=["POST"])
|
||||||
|
def issue_token():
|
||||||
|
return authorization.create_token_response()
|
|
@ -35,18 +35,20 @@ def generate_user_info(user, scope):
|
||||||
|
|
||||||
|
|
||||||
def create_authorization_code(client, grant_user, request):
|
def create_authorization_code(client, grant_user, request):
|
||||||
raise NotImplementedError()
|
|
||||||
code = gen_salt(48)
|
|
||||||
nonce = request.data.get("nonce")
|
nonce = request.data.get("nonce")
|
||||||
item = AuthorizationCode(
|
now = datetime.datetime.now()
|
||||||
code=code,
|
code = AuthorizationCode(
|
||||||
client_id=client.client_id,
|
oauthCode=gen_salt(48),
|
||||||
redirect_uri=request.redirect_uri,
|
oauthSubject=grant_user,
|
||||||
scope=request.scope,
|
oauthClientID=client.oauthClientID,
|
||||||
user_id=grant_user.id,
|
oauthRedirectURI=request.redirect_uri or client.oauthRedirectURIs[0],
|
||||||
nonce=nonce,
|
oauthScope=request.scope,
|
||||||
|
oauthNonce=nonce or "nonce", #TODO
|
||||||
|
oauthAuthorizationDate=now.strftime("%Y%m%d%H%M%SZ"),
|
||||||
|
oauthAuthorizationLifetime=str(84000),
|
||||||
)
|
)
|
||||||
return code
|
code.save()
|
||||||
|
return code.oauthCode
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationCodeGrant(_AuthorizationCodeGrant):
|
class AuthorizationCodeGrant(_AuthorizationCodeGrant):
|
||||||
|
@ -54,11 +56,11 @@ class AuthorizationCodeGrant(_AuthorizationCodeGrant):
|
||||||
return create_authorization_code(client, grant_user, request)
|
return create_authorization_code(client, grant_user, request)
|
||||||
|
|
||||||
def parse_authorization_code(self, code, client):
|
def parse_authorization_code(self, code, client):
|
||||||
item = AuthorizationCode.query.filter_by(
|
item = AuthorizationCode.filter(
|
||||||
code=code, client_id=client.client_id
|
oauthCode=code, oauthClientID=client.oauthClientID
|
||||||
).first()
|
)
|
||||||
if item and not item.is_expired():
|
if item and not item[0].get_expires_at() < datetime.datetime.now():
|
||||||
return item
|
return item[0]
|
||||||
|
|
||||||
def delete_authorization_code(self, authorization_code):
|
def delete_authorization_code(self, authorization_code):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
from flask import Blueprint, request, session
|
from flask import Blueprint, request, session
|
||||||
from flask import render_template, redirect, jsonify
|
from flask import render_template, redirect, jsonify
|
||||||
from authlib.oauth2 import OAuth2Error
|
|
||||||
from .models import User, Client
|
from .models import User, Client
|
||||||
from .oauth2utils import authorization, require_oauth
|
from .oauth2utils import require_oauth
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint(__name__, "home")
|
bp = Blueprint(__name__, "home")
|
||||||
|
|
||||||
|
|
||||||
def current_user():
|
|
||||||
if "user_dn" in session:
|
|
||||||
return User.get(session["user_dn"])
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/", methods=("GET", "POST"))
|
@bp.route("/", methods=("GET", "POST"))
|
||||||
def home():
|
def home():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -27,35 +20,8 @@ def home():
|
||||||
session["user_dn"] = user.dn
|
session["user_dn"] = user.dn
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
user = current_user()
|
|
||||||
if user:
|
|
||||||
clients = Client.filter()
|
clients = Client.filter()
|
||||||
else:
|
return render_template("home.html", clients=clients)
|
||||||
clients = []
|
|
||||||
|
|
||||||
return render_template("home.html", user=user, clients=clients)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/oauth/authorize", methods=["GET", "POST"])
|
|
||||||
def authorize():
|
|
||||||
user = current_user()
|
|
||||||
if request.method == "GET":
|
|
||||||
try:
|
|
||||||
grant = authorization.validate_consent_request(end_user=user)
|
|
||||||
except OAuth2Error as error:
|
|
||||||
return jsonify(dict(error.get_body()))
|
|
||||||
return render_template("authorize.html", user=user, grant=grant)
|
|
||||||
|
|
||||||
if not user and "username" in request.form:
|
|
||||||
username = request.form.get("username")
|
|
||||||
user = User.get(username)
|
|
||||||
|
|
||||||
if request.form["confirm"]:
|
|
||||||
grant_user = user
|
|
||||||
else:
|
|
||||||
grant_user = None
|
|
||||||
|
|
||||||
return authorization.create_authorization_response(grant_user=grant_user)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/logout")
|
@bp.route("/logout")
|
||||||
|
@ -64,11 +30,6 @@ def logout():
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/oauth/token", methods=["POST"])
|
|
||||||
def issue_token():
|
|
||||||
return authorization.create_token_response()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/api/me")
|
@bp.route("/api/me")
|
||||||
@require_oauth("profile")
|
@require_oauth("profile")
|
||||||
def api_me():
|
def api_me():
|
||||||
|
|
|
@ -1,28 +1,33 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ui segment">
|
<div class="ui center aligned segment">
|
||||||
<p>The application <strong>{{grant.client.client_name}}</strong> is requesting:
|
{% if client.oauthLogoURI %}
|
||||||
<strong>{{ grant.request.scope }}</strong>
|
<img class="ui centered tiny image" src="{{ client.oauthLogoURI }}" alt="{{ client.oauthClientName }}">
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
from You - a.k.a. <strong>{{ user.username }}</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form action="" method="post">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="confirm">
|
|
||||||
<span>Consent?</span>
|
|
||||||
</label>
|
|
||||||
{% if not user %}
|
|
||||||
<p>You haven't logged in. Log in with:</p>
|
|
||||||
<div>
|
|
||||||
<input type="text" name="username">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br>
|
|
||||||
<button>Submit</button>
|
<p>{{ gettext('The application %(name)s is requesting access to:', name=client.oauthClientName) }}</p>
|
||||||
</form>
|
<p>
|
||||||
|
<strong>{{ grant.request.scope }}</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>{{ gettext('from: %(user)s', user=user.name) }}</p>
|
||||||
|
|
||||||
|
<div class="ui buttons">
|
||||||
|
<form action="{{ request.url }}" method="post">
|
||||||
|
<input type="hidden" name="answer" value="deny" />
|
||||||
|
<input type="submit" class="ui negative button" value="{% trans %}Deny{% endtrans %}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form action="{{ request.url }}" method="post">
|
||||||
|
<input type="hidden" name="answer" value="logout" />
|
||||||
|
<input type="submit" class="ui button" value="{% trans %}Switch user{% endtrans %}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form action="{{ request.url }}" method="post">
|
||||||
|
<input type="hidden" name="answer" value="accept" />
|
||||||
|
<input type="submit" class="ui positive button" value="{% trans %}Accept{% endtrans %}" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
29
web/templates/login.html
Normal file
29
web/templates/login.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% import 'fomanticui.j2' as sui %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="ui clearing segment">
|
||||||
|
{% if logo_url %}
|
||||||
|
<img class="ui tiny centered image" src="{{ logo_url }}" alt="{{ website_name }}">
|
||||||
|
{% else %}
|
||||||
|
<i class="massive sign in icon"></i>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h2 class="ui center aligned header">
|
||||||
|
<div class="content">
|
||||||
|
{{ _("Sign in at %(website)s", website=website_name) }}
|
||||||
|
</div>
|
||||||
|
<div class="sub header">{% trans %}Log-in and manage your authorizations.{% endtrans %}</div>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="ui attached message {{ category }}">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{{ sui.render_form(form, _("Sign in"), action=request.url) }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in a new issue