mirror of
https://github.com/simple-login/app.git
synced 2024-09-20 15:05:59 +08:00
user can choose custom email in authorize
This commit is contained in:
parent
ac8fa33a83
commit
00f858c8c1
|
@ -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 %}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue