user can choose custom email in authorize

This commit is contained in:
Son NK 2019-07-22 19:17:57 +02:00 committed by Son NK
parent ac8fa33a83
commit 00f858c8c1
3 changed files with 185 additions and 89 deletions

View File

@ -5,99 +5,154 @@
{% endblock %}
{% block default_content %}
<div class="col-md-6 offset-md-3">
<form class="card" method="post">
<div class="card-body p-6">
<!-- User has already authorized this client -->
{% if client_user %}
<div class="card-title">
You have already authorized <b>{{ client.name }}</b>.
</div>
<form class="card" method="post" style="max-width: 40rem; margin: auto">
<div class="card-body p-6">
<!-- User has already authorized this client -->
{% if client_user %}
<div class="card-title">
You have already authorized <b>{{ client.name }}</b>.
</div>
<hr>
<hr>
<div class="mb-4">
<b>{{ client.name }}</b> has access to the following information:
</div>
<div>
{% for scope in client.get_scopes() %}
<div>
{% if scope == Scope.AVATAR_URL and user_info[scope.value] %}
avatar: <img src="{{ user_info[scope.value] }}" class="avatar">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
<a href="mailto:{{ user_info[scope.value] }}">
{{ user_info[scope.value] }}
</a>
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ user_info[scope.value] }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<div class="card-title">
<b>{{ client.name }}</b> will receive your following information:
</div>
<div>
{% for scope in client.get_scopes() %}
<div style="display: flex; align-items: center; margin: .5rem 0; line-height: 1.3rem">
{% if scope == Scope.AVATAR_URL and current_user.profile_picture_id %}
avatar: <img src="{{ current_user.profile_picture_url() }}" class="avatar" style="margin: 10px 0px">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
A random alias or your original email
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ current_user.name }}</b>
{% endif %}
</div>
{% endfor %}
<div class="mb-4">
<b>{{ client.name }}</b> has access to the following information:
</div>
<div>
{% for scope in client.get_scopes() %}
<div>
{% if scope == Scope.AVATAR_URL and user_info[scope.value] %}
avatar: <img src="{{ user_info[scope.value] }}" class="avatar">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
<a href="mailto:{{ user_info[scope.value] }}">
{{ user_info[scope.value] }}
</a>
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ user_info[scope.value] }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
{% if client.icon_id %}
<div class="text-center">
<img src="{{ client.get_icon_url() }}" class="small-client-icon">
</div>
{% endif %}
{% if client_user %}
<div class="form-footer">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow"
class="btn btn-success">Allow
</button>
<div class="text-center mt-4">
<b>{{ client.name }}</b> will receive the following info
</div>
<a class="btn btn-light" href="javascript:history.back()">
Cancel
</a>
</div>
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Email</label>
</div>
{% else %}
<div class="form-group" style="margin-top: 1rem">
<div class="custom-controls-stacked">
<label class="custom-control custom-checkbox">
<input type="checkbox" name="gen-email"
class="custom-control-input" checked>
<span class="custom-control-label">Use email alias</span>
</label>
</div>
<div class="col-md-9">
<select class="custom-select custom-select" name="suggested-email">
<option selected value="{{ suggested_email }}">{{ suggested_email }}</option>
<option value="{{ personal_email }}">{{ personal_email }}</option>
{% for email in other_emails %}
<option value="{{ email }}">{{ email }}</option>
{% endfor %}
</select>
<small class="form-text text-muted">
If checked, an email alias will be used. <br>
Otherwise, your personal email will be communicated to <b>{{ client.name }}</b>.
</small>
{% if current_user.can_create_custom_email() %}
<div class="mt-2">OR</div>
<div style="display: flex; align-items: center" class="mt-2">
<input class="form-control" style="flex-grow: 2" name="custom-email-prefix">
<input type="hidden" name="email-suffix" value="{{ email_suffix }}">
<div class="ml-2">
.{{ email_suffix }}@simplelogin.co
</div>
</div>
{% endif %}
</div>
</div>
<div class="form-footer">
<div class="btn-group btn-block" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow"
class="btn btn-success">Allow
</button>
<div class="row mt-4">
<div class="col-md-3 text-left">
<label>Name</label>
</div>
<div class="col-md-9">
{{ current_user.name }}
</div>
</div>
<button type="submit" name="button" value="deny"
class="btn btn-light">Deny
</button>
{% if current_user.profile_picture_id %}
<div class="row mt-4">
<div class="col-md-3 text-right">
<label>Avatar</label>
</div>
<div class="col-md-9">
<img class="profile-picture" href="{{ current_user.profile_picture_url() }}">
</div>
</div>
{% endif %}
</div>
</form>
</div>
<hr>
<div class="card-title">
<b>{{ client.name }}</b> will receive your following information:
</div>
<div>
{% for scope in client.get_scopes() %}
<div style="display: flex; align-items: center; margin: .5rem 0; line-height: 1.3rem">
{% if scope == Scope.AVATAR_URL and current_user.profile_picture_id %}
avatar: <img src="{{ current_user.profile_picture_url() }}" class="avatar" style="margin: 10px 0px">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
A random alias or your original email
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ current_user.name }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{% if client_user %}
<div class="form-footer">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow"
class="btn btn-success">Allow
</button>
<a class="btn btn-light" href="javascript:history.back()">
Cancel
</a>
</div>
</div>
{% else %}
<div class="form-group" style="margin-top: 1rem">
<div class="custom-controls-stacked">
<label class="custom-control custom-checkbox">
<input type="checkbox" name="gen-email"
class="custom-control-input" checked>
<span class="custom-control-label">Use email alias</span>
</label>
</div>
<small class="form-text text-muted">
If checked, an email alias will be used. <br>
Otherwise, your personal email will be communicated to <b>{{ client.name }}</b>.
</small>
</div>
<div class="form-footer">
<div class="btn-group btn-block" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow"
class="btn btn-success">Allow
</button>
<button type="submit" name="button" value="deny"
class="btn btn-light">Deny
</button>
</div>
</div>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -5,6 +5,7 @@ from urllib.parse import urlparse
from flask import request, render_template, redirect
from flask_login import current_user
from app.config import EMAIL_DOMAIN
from app.extensions import db
from app.jose_utils import make_id_token
from app.log import LOG
@ -18,7 +19,7 @@ from app.models import (
)
from app.oauth.base import oauth_bp
from app.oauth_models import get_response_types, ResponseType, Scope
from app.utils import random_string, encode_url
from app.utils import random_string, encode_url, convert_to_id
@oauth_bp.route("/authorize", methods=["GET", "POST"])
@ -63,6 +64,8 @@ def authorize():
# redirect from client website
if request.method == "GET":
if current_user.is_authenticated:
suggested_email, other_emails, email_suffix = None, [], None
# user has already allowed this client
client_user: ClientUser = ClientUser.get_by(
client_id=client.id, user_id=current_user.id
@ -71,6 +74,9 @@ def authorize():
if client_user:
LOG.debug("user %s has already allowed client %s", current_user, client)
user_info = client_user.get_user_info()
else:
suggested_email, other_emails = current_user.suggested_emails()
email_suffix = random_string(6)
return render_template(
"oauth/authorize.html",
@ -78,6 +84,10 @@ def authorize():
user_info=user_info,
client_user=client_user,
Scope=Scope,
suggested_email=suggested_email,
personal_email=current_user.email,
other_emails=other_emails,
email_suffix=email_suffix,
)
else:
# after user logs in, redirect user back to this page
@ -88,8 +98,6 @@ def authorize():
Scope=Scope,
)
else: # user allows or denies
gen_new_email = request.form.get("gen-email") == "on"
if request.form.get("button") == "deny":
LOG.debug("User %s denies Client %s", current_user, client)
final_redirect_uri = f"{redirect_uri}?error=deny&state={state}"
@ -98,15 +106,40 @@ def authorize():
LOG.debug("User %s allows Client %s", current_user, client)
client_user = ClientUser.get_by(client_id=client.id, user_id=current_user.id)
# user has already allowed this client
# user has already allowed this client, user cannot change information
if client_user:
LOG.d("user %s has already allowed client %s", current_user, client)
# User cannot choose to gen new email
gen_new_email = False
else:
email_suffix = request.form.get("email-suffix")
custom_email_prefix = request.form.get("custom-email-prefix")
chosen_email = request.form.get("suggested-email")
gen_email = None
if custom_email_prefix:
# check if user can generate custom email
if not current_user.can_create_custom_email():
raise Exception(f"User {current_user} cannot create custom email")
email = f"{convert_to_id(custom_email_prefix)}.{email_suffix}@{EMAIL_DOMAIN}"
LOG.d("create custom email alias %s for user %s", email, current_user)
gen_email = GenEmail.create(email=email, user_id=current_user.id)
db.session.flush()
else: # user picks an email from suggestion
if chosen_email != current_user.email:
gen_email = GenEmail.get_by(email=chosen_email)
if not gen_email:
gen_email = GenEmail.create(
email=chosen_email, user_id=current_user.id
)
db.session.flush()
client_user = ClientUser.create(
client_id=client.id, user_id=current_user.id
)
if gen_email:
client_user.gen_email_id = gen_email.id
db.session.flush()
LOG.d("create client-user for client %s, user %s", client, current_user)
@ -148,9 +181,6 @@ def authorize():
elif response_type == ResponseType.ID_TOKEN:
redirect_args["id_token"] = make_id_token(client_user)
if gen_new_email:
client_user.gen_email_id = create_or_choose_gen_email(current_user).id
db.session.commit()
# construct redirect_uri with redirect_args

View File

@ -17,5 +17,16 @@
/* round the edges to a circle with border radius 1/2 container size */
border-radius: 10%;
margin-top: 10px;
}
.small-client-icon {
/* make a square container */
width: 40px;
height: 40px;
/* round the edges to a circle with border radius 1/2 container size */
border-radius: 10%;
margin-top: 10px;
}