def two_factor_email(token): if current_user.is_authenticated: return redirect_when_logged_in(current_user.id) # checks url is valid, and hasn't timed out try: token_data = json.loads( check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_2FA_EXPIRY_SECONDS'])) except SignatureExpired as exc: # lets decode again, without the expiry, to get the user id out orig_data = json.loads( check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], None)) session['user_details'] = {'id': orig_data['user_id']} flash( "The link in the email we sent you has expired. We’ve sent you a new one." ) return redirect(url_for('.resend_email_link')) user_id = token_data['user_id'] # checks if code was already used logged_in, msg = user_api_client.check_verify_code( user_id, token_data['secret_code'], "email") if not logged_in: flash("This link has already been used") session['user_details'] = {'id': user_id} return redirect(url_for('.resend_email_link')) return log_in_user(user_id)
def test_should_throw_exception_when_token_is_tampered_with(): import uuid token = generate_token(str(uuid.uuid4()), 'secret-key', 'dangerous-salt') try: check_token(token + 'qerqwer', 'secret-key', 'dangerous-salt', 30) fail() except BadSignature: pass
def verify_email(token): try: token_data = check_token( token, current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], current_app.config["EMAIL_EXPIRY_SECONDS"], ) except SignatureExpired: flash( _("The security code in the email we sent you has expired. We’ve sent you a new one." )) return redirect(url_for("main.resend_email_verification")) # token contains json blob of format: {'user_id': '...', 'secret_code': '...'} (secret_code is unused) token_data = json.loads(token_data) user = User.from_id(token_data["user_id"]) if not user: abort(404) if user.is_active: flash(_("That verification link has expired.")) return redirect(url_for("main.sign_in")) session["user_details"] = {"email": user.email_address, "id": user.id} user.send_verify_code() return redirect(url_for("main.verify"))
def validate_invitation_token(invitation_type, token): max_age_seconds = 60 * 60 * 24 * current_app.config[ "INVITATION_EXPIRATION_DAYS"] try: invited_user_id = check_token( token, current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], max_age_seconds, ) except SignatureExpired: errors = {"invitation": "invitation expired"} raise InvalidRequest(errors, status_code=400) except BadData: errors = {"invitation": "bad invitation link"} raise InvalidRequest(errors, status_code=400) if invitation_type == "service": invited_user = get_invited_user_by_id(invited_user_id) return jsonify(data=invited_user_schema.dump(invited_user).data), 200 elif invitation_type == "organisation": invited_user = dao_get_invited_organisation_user(invited_user_id) return jsonify(data=invited_user.serialize()), 200 else: raise InvalidRequest( "Unrecognised invitation type: {}".format(invitation_type))
def new_password(token): from notifications_utils.url_safe_token import check_token try: token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['TOKEN_MAX_AGE_SECONDS']) except SignatureExpired: flash( 'The link in the email we sent you has expired. Enter your email address to resend.' ) return redirect(url_for('.forgot_password')) email_address = json.loads(token_data)['email'] user = user_api_client.get_user_by_email(email_address) if user.password_changed_at and datetime.strptime(user.password_changed_at, '%Y-%m-%d %H:%M:%S.%f') > \ datetime.strptime(json.loads(token_data)['created_at'], '%Y-%m-%d %H:%M:%S.%f'): flash('The link in the email has already been used') return redirect(url_for('main.index')) form = NewPasswordForm() if form.validate_on_submit(): user_api_client.send_verify_code(user.id, 'sms', user.mobile_number) session['user_details'] = { 'id': user.id, 'email': user.email_address, 'password': form.new_password.data } return redirect(url_for('main.two_factor')) else: return render_template('views/new-password.html', token=token, form=form, user=user)
def new_password(token): from notifications_utils.url_safe_token import check_token try: token_data = check_token( token, current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], current_app.config["TOKEN_MAX_AGE_SECONDS"], ) except SignatureExpired: flash("The link in the email we sent you has expired. Enter your email address to resend.") return redirect(url_for(".forgot_password")) email_address = json.loads(token_data)["email"] user = user_api_client.get_user_by_email(email_address) if user.password_changed_at and datetime.strptime( user.password_changed_at, "%Y-%m-%d %H:%M:%S.%f" ) > datetime.strptime(json.loads(token_data)["created_at"], "%Y-%m-%d %H:%M:%S.%f"): flash("The link in the email has already been used") return redirect(url_for("main.index")) form = NewPasswordForm() if form.validate_on_submit(): user_api_client.send_verify_code(user.id, "sms", user.mobile_number) session["user_details"] = {"id": user.id, "email": user.email_address, "password": form.new_password.data} return redirect(url_for("main.two_factor")) else: return render_template("views/new-password.html", token=token, form=form, user=user)
def validate_invitation_token(invitation_type, token): max_age_seconds = 60 * 60 * 24 * current_app.config[ 'INVITATION_EXPIRATION_DAYS'] try: invited_user_id = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], max_age_seconds) except SignatureExpired: errors = { 'invitation': 'Your invitation to GOV.UK Notify has expired. ' 'Please ask the person that invited you to send you another one' } raise InvalidRequest(errors, status_code=400) except BadData: errors = { 'invitation': 'Something’s wrong with this link. Make sure you’ve copied the whole thing.' } raise InvalidRequest(errors, status_code=400) if invitation_type == 'service': invited_user = get_invited_user_by_id(invited_user_id) return jsonify(data=invited_user_schema.dump(invited_user).data), 200 elif invitation_type == 'organisation': invited_user = dao_get_invited_organisation_user(invited_user_id) return jsonify(data=invited_user.serialize()), 200 else: raise InvalidRequest( "Unrecognised invitation type: {}".format(invitation_type))
def verify_email(token): try: token_data = check_token( token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS'] ) except SignatureExpired: flash("The link in the email we sent you has expired. We've sent you a new one.") return redirect(url_for('main.resend_email_verification')) # token contains json blob of format: {'user_id': '...', 'secret_code': '...'} (secret_code is unused) token_data = json.loads(token_data) user = user_api_client.get_user(token_data['user_id']) if not user: abort(404) if user.is_active: flash("That verification link has expired.") return redirect(url_for('main.sign_in')) session['user_details'] = {"email": user.email_address, "id": user.id} user_api_client.send_verify_code(user.id, 'sms', user.mobile_number) return redirect(url_for('main.verify'))
def verify_email(token): try: token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS']) token_data = json.loads(token_data) verified = user_api_client.check_verify_code(token_data['user_id'], token_data['secret_code'], 'email') user = user_api_client.get_user(token_data['user_id']) if not user: abort(404) if user.is_active: flash("That verification link has expired.") return redirect(url_for('main.sign_in')) session['user_details'] = {"email": user.email_address, "id": user.id} if verified[0]: user_api_client.send_verify_code(user.id, 'sms', user.mobile_number) return redirect('verify') else: if verified[1] == 'Code has expired': flash("The link in the email we sent you has expired. We've sent you a new one.") return redirect(url_for('main.resend_email_verification')) else: message = "There was a problem verifying your account. Error message: '{}'".format(verified[1]) flash(message) return redirect(url_for('main.index')) except SignatureExpired: flash('The link in the email we sent you has expired') return redirect(url_for('main.resend_email_verification'))
def new_password(token): try: token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS']) except SignatureExpired: flash('The link in the email we sent you has expired. Enter your email address to resend.') return redirect(url_for('.forgot_password')) email_address = json.loads(token_data)['email'] user = user_api_client.get_user_by_email(email_address) if user.password_changed_at and datetime.strptime(user.password_changed_at, '%Y-%m-%d %H:%M:%S.%f') > \ datetime.strptime(json.loads(token_data)['created_at'], '%Y-%m-%d %H:%M:%S.%f'): flash('The link in the email has already been used') return redirect(url_for('main.index')) form = NewPasswordForm() if form.validate_on_submit(): user_api_client.reset_failed_login_count(user.id) session['user_details'] = { 'id': user.id, 'email': user.email_address, 'password': form.new_password.data} if user.auth_type == 'email_auth': # they've just clicked an email link, so have done an email auth journey anyway. Just log them in. return log_in_user(user.id) else: # send user a 2fa sms code user_api_client.send_verify_code(user.id, 'sms', user.mobile_number) return redirect(url_for('main.two_factor')) else: return render_template('views/new-password.html', token=token, form=form, user=user)
def two_factor_email(token): redirect_url = request.args.get('next') if current_user.is_authenticated: return redirect_when_logged_in( platform_admin=current_user.platform_admin) # checks url is valid, and hasn't timed out try: token_data = json.loads( check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_2FA_EXPIRY_SECONDS'])) except SignatureExpired: return render_template('views/email-link-invalid.html', redirect_url=redirect_url) user_id = token_data['user_id'] # checks if code was already used logged_in, msg = user_api_client.check_verify_code( user_id, token_data['secret_code'], "email") if not logged_in: return render_template('views/email-link-invalid.html', redirect_url=redirect_url) return log_in_user(user_id)
def test_return_none_when_token_is_expired(): max_age = -1000 payload = 'some_payload' token = generate_token(payload, 'secret-key', 'dangerous-salt') try: assert check_token(token, 'secret-key', 'dangerous-salt', max_age) is None fail('Expected a SignatureExpired exception') except SignatureExpired: pass
def user_profile_email_confirm(token): token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS']) token_data = json.loads(token_data) user = User.from_id(token_data['user_id']) user.update(email_address=token_data['email']) session.pop(NEW_EMAIL, None) return redirect(url_for('.user_profile'))
def test_return_none_when_token_is_expired(): max_age = -1000 payload = 'some_payload' token = generate_token(payload, 'secret-key', 'dangerous-salt') token = urllib.parse.unquote(token) try: assert check_token(token, 'secret-key', 'dangerous-salt', max_age) is None fail('Expected a SignatureExpired exception') except SignatureExpired: pass
def user_profile_email_confirm(token): token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS']) token_data = json.loads(token_data) user_id = token_data['user_id'] new_email = token_data['email'] user_api_client.update_user_attribute(user_id, email_address=new_email) session.pop(NEW_EMAIL, None) return redirect(url_for('.user_profile'))
def user_profile_email_confirm(token): token_data = check_token( token, current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], current_app.config["EMAIL_EXPIRY_SECONDS"], ) token_data = json.loads(token_data) user = User.from_id(token_data["user_id"]) user.update(email_address=token_data["email"]) session.pop(NEW_EMAIL, None) return redirect(url_for(".user_profile"))
def get_invited_user_by_token(token): max_age_seconds = 60 * 60 * 24 * current_app.config['INVITATION_EXPIRATION_DAYS'] try: invited_user_id = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], max_age_seconds) except SignatureExpired: errors = {'invitation': ['Your invitation to GOV.UK Notify has expired. ' 'Please ask the person that invited you to send you another one']} raise InvalidRequest(errors, status_code=400) invited_user = get_invited_user_by_id(invited_user_id) return jsonify(data=invited_user_schema.dump(invited_user).data), 200
def new_password(token): try: token_data = check_token( token, current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], current_app.config["EMAIL_EXPIRY_SECONDS"], ) except SignatureExpired: flash(_("The security code in the email we sent you has expired. Enter your email address to re-send.")) return redirect(url_for(".forgot_password")) email_address = json.loads(token_data)["email"] user = User.from_email_address(email_address) if user.password_changed_at and datetime.strptime(user.password_changed_at, "%Y-%m-%d %H:%M:%S.%f") > datetime.strptime( json.loads(token_data)["created_at"], "%Y-%m-%d %H:%M:%S.%f" ): flash(_("The security code in the email has already been used")) return redirect(url_for("main.index")) form = NewPasswordForm() if form.validate_on_submit(): user.reset_failed_login_count() session["user_details"] = { "id": user.id, "email": user.email_address, "password": form.new_password.data, } if user.auth_type == "email_auth": # they've just clicked an email link, so have done an email auth journey anyway. Just log them in. return log_in_user(user.id) else: # send user a 2fa sms code user.send_verify_code() return redirect(url_for("main.two_factor_sms_sent")) else: return render_template("views/new-password.html", token=token, form=form, user=user)
def verify_email(token): try: token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS']) token_data = json.loads(token_data) verified = user_api_client.check_verify_code(token_data['user_id'], token_data['secret_code'], 'email') user = user_api_client.get_user(token_data['user_id']) if not user: abort(404) if user.is_active: flash("That verification link has expired.") return redirect(url_for('main.sign_in')) session['user_details'] = {"email": user.email_address, "id": user.id} if verified[0]: user_api_client.send_verify_code(user.id, 'sms', user.mobile_number) return redirect('verify') else: if verified[1] == 'Code has expired': flash( "The link in the email we sent you has expired. We've sent you a new one." ) return redirect(url_for('main.resend_email_verification')) else: message = "There was a problem verifying your account. Error message: '{}'".format( verified[1]) flash(message) return redirect(url_for('main.index')) except SignatureExpired: flash('The link in the email we sent you has expired') return redirect(url_for('main.resend_email_verification'))
def reverify_email_token(token): try: token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['EMAIL_EXPIRY_SECONDS']) except SignatureExpired: flash( 'The link in the email we sent you has expired. We\'ve sent you a new one.' ) return redirect(url_for('main.resend_email_reverification')) token_data = json.loads(token_data) user = user_api_client.get_user(token_data['user_id']) if not user: abort(404) session['user_details'] = { 'email': user.email_address, 'id': user.id, 'set_last_verified_at': True, } return log_in_user(user.id)
def test_should_return_payload_from_signed_token(): payload = '*****@*****.**' token = generate_token(payload, 'secret-key', 'dangerous-salt') assert payload == check_token(token, 'secret-key', 'dangerous-salt', 30)
def accept_invite(token): try: check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'], current_app.config['INVITATION_EXPIRY_SECONDS']) except SignatureExpired: errors = [ 'Your invitation to GOV.UK Notify has expired. ' 'Please ask the person that invited you to send you another one' ] return render_template("error/400.html", message=errors), 400 invited_user = invite_api_client.check_token(token) if not current_user.is_anonymous and current_user.email_address.lower( ) != invited_user.email_address.lower(): message = Markup(""" You’re signed in as {}. This invite is for another email address. <a href={}>Sign out</a> and click the link again to accept this invite. """.format(current_user.email_address, url_for("main.sign_out", _external=True))) flash(message=message) abort(403) if invited_user.status == 'cancelled': from_user = user_api_client.get_user(invited_user.from_user) service = service_api_client.get_service(invited_user.service)['data'] return render_template('views/cancelled-invitation.html', from_user=from_user.name, service_name=service['name']) if invited_user.status == 'accepted': session.pop('invited_user', None) return redirect( url_for('main.service_dashboard', service_id=invited_user.service)) session['invited_user'] = invited_user.serialize() existing_user = user_api_client.get_user_by_email_or_none( invited_user.email_address) service_users = user_api_client.get_users_for_service(invited_user.service) if existing_user: invite_api_client.accept_invite(invited_user.service, invited_user.id) if existing_user in service_users: return redirect( url_for('main.service_dashboard', service_id=invited_user.service)) else: service = service_api_client.get_service( invited_user.service)['data'] # if the service you're being added to can modify auth type, then check if this is relevant if 'email_auth' in service['permissions'] and ( # they have a phone number, we want them to start using it. if they dont have a mobile we just # ignore that option of the invite (existing_user.mobile_number and invited_user.auth_type == 'sms_auth') or # we want them to start sending emails. it's always valid, so lets always update invited_user.auth_type == 'email_auth'): user_api_client.update_user_attribute( existing_user.id, auth_type=invited_user.auth_type) user_api_client.add_user_to_service(invited_user.service, existing_user.id, invited_user.permissions) return redirect( url_for('main.service_dashboard', service_id=invited_user.service)) else: return redirect(url_for('main.register_from_invite'))
def test_should_return_payload_from_signed_token(): payload = '*****@*****.**' token = generate_token(payload, 'secret-key', 'dangerous-salt') token = urllib.parse.unquote(token) assert payload == check_token(token, 'secret-key', 'dangerous-salt', 30)