Exemple #1
0
def verify_email_complete():
    """View for concluding the login with Hydra after verifying an email address.

    The token ensures that we can only get here from the verify email view.
    """
    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(
            token, 'account.verify_email_complete')

        form = AutoLoginForm()
        user_id = params.get('user_id')

        if request.method == 'POST':
            try:
                validate_auto_login(user_id)
                redirect_to = hydra_admin.accept_login_request(
                    challenge, user_id)

            except x.ODPIdentityError as e:
                # any validation error => reject login
                redirect_to = hydra_admin.reject_login_request(
                    challenge, e.error_code, e.error_description)

            return redirect(redirect_to)

        return render_template('verify_email_complete.html',
                               form=form,
                               token=token,
                               brand=brand)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #2
0
def profile():
    """View for updating user profile info."""

    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(
            token, 'account.profile')

        user_id = params.get('user_id')
        user_info = get_user_profile(user_id)
        form = ProfileForm(**user_info)

        if request.method == 'POST':
            try:
                validate_auto_login(user_id)
                update_user_profile(user_id, **form.data)
                redirect_to = hydra_admin.accept_login_request(
                    challenge, user_id)

            except x.ODPIdentityError as e:
                # any validation error => reject login
                redirect_to = hydra_admin.reject_login_request(
                    challenge, e.error_code, e.error_description)

            return redirect(redirect_to)

        return render_template('profile.html',
                               form=form,
                               token=token,
                               brand=brand)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #3
0
def login():
    """
    Implements the login provider component of the Hydra login workflow.
    Hydra redirects to this endpoint based on the ``URLS_LOGIN`` environment
    variable configured on the Hydra server.
    """
    try:
        challenge = request.args.get('login_challenge')
        login_request = hydra_admin.get_login_request(challenge)
        mode = LoginMode.from_login_request(login_request)
        brand = Brand.from_login_request(login_request).value

        if mode == LoginMode.LOGIN:
            target_endpoint = 'login.login'
        elif mode == LoginMode.SIGNUP:
            target_endpoint = 'signup.signup'
        else:
            raise ValueError

        token = encode_token('login', challenge, brand)
        redirect_to = url_for(target_endpoint, token=token)
        return redirect(redirect_to)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #4
0
def verify_email():
    """Target for email verification links.

    The token ensures that this view is only accessible from a verification email.
    """
    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(
            token, 'account.verify_email')

        email = params.get('email')
        try:
            user_id = validate_email_verification(email)
            update_user_verified(user_id, True)
            flash("Your email address has been verified.")

            complete_token = encode_token('account.verify_email_complete',
                                          challenge,
                                          brand,
                                          user_id=user_id)
            redirect_to = url_for('.verify_email_complete',
                                  token=complete_token)

        except x.ODPIdentityError as e:
            # any validation error => reject login
            redirect_to = hydra_admin.reject_login_request(
                challenge, e.error_code, e.error_description)

        return redirect(redirect_to)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
def forgot_password():
    """View for sending a password reset email.

    The token ensures that we can only access this view in the context
    of the Hydra login workflow.
    """
    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(token, 'login')

        form = ForgotPasswordForm()
        sent = False

        if request.method == 'POST':
            if form.validate():
                email = form.email.data
                try:
                    user_id = validate_forgot_password(email)
                    name = get_user_profile_by_email(email)['name']
                    send_password_reset_email(email, name, challenge, brand)
                    sent = True

                except x.ODPUserNotFound:
                    form.email.errors.append("The email address is not associated with any user account.")

                except x.ODPIdentityError as e:
                    # any other validation error => reject login
                    redirect_to = hydra_admin.reject_login_request(challenge, e.error_code, e.error_description)
                    return redirect(redirect_to)

        return render_template('forgot_password.html', form=form, token=token, brand=brand, sent=sent)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #6
0
def signup():
    """User signup view.

    The token ensures that we can only access this view in the context
    of the Hydra login workflow.
    """
    token = request.args.get('token')
    try:
        # the token scope 'login' here is correct - it enables us to easily
        # switch between login and signup using the same token
        login_request, challenge, brand, params = decode_token(token, 'login')

        form = SignupForm()
        try:
            if request.method == 'GET':
                # if the user is already authenticated with Hydra, their user id is
                # associated with the login challenge; we cannot then associate a new
                # user id with the same login challenge
                authenticated = login_request['skip']
                if authenticated:
                    raise x.ODPSignupAuthenticatedUser

            else:  # POST
                if form.validate():
                    email = form.email.data
                    password = form.password.data
                    name = form.name.data
                    try:
                        create_user_account(email, password, name)

                        # the signup (and login) is completed via email verification
                        send_verification_email(email, name, challenge, brand)
                        verify_token = encode_token('signup.verify', challenge, brand, email=email, name=name)
                        redirect_to = url_for('.verify', token=verify_token)

                        return redirect(redirect_to)

                    except x.ODPEmailInUse:
                        form.email.errors.append("The email address is already associated with a user account.")

                    except x.ODPPasswordComplexityError:
                        form.password.errors.append("The password does not meet the minimum complexity requirements.")
                        flash(password_complexity_description(), category='info')

            return render_template('signup.html', form=form, token=token, brand=brand, enable_google=config.GOOGLE.ENABLE)

        except x.ODPIdentityError as e:
            # any other validation error (e.g. user already authenticated) => reject login
            redirect_to = hydra_admin.reject_login_request(challenge, e.error_code, e.error_description)
            return redirect(redirect_to)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #7
0
def reset_password():
    """Target for password reset links.

    The token ensures that this view is only accessible from a password reset email.
    """
    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(
            token, 'account.reset_password')

        form = ResetPasswordForm()
        email = params.get('email')

        if request.method == 'POST':
            if form.validate():
                password = form.password.data
                redirect_to = None
                try:
                    user_id = validate_password_reset(email, password)
                    update_user_password(user_id, password)
                    flash("Your password has been changed.")

                    complete_token = encode_token(
                        'account.reset_password_complete',
                        challenge,
                        brand,
                        user_id=user_id)
                    redirect_to = url_for('.reset_password_complete',
                                          token=complete_token)

                except x.ODPPasswordComplexityError:
                    form.password.errors.append(
                        "The password does not meet the minimum complexity requirements."
                    )
                    flash(password_complexity_description(), category='info')

                except x.ODPIdentityError as e:
                    # any other validation error => reject login
                    redirect_to = hydra_admin.reject_login_request(
                        challenge, e.error_code, e.error_description)

                if redirect_to:
                    return redirect(redirect_to)

        return render_template('reset_password.html',
                               form=form,
                               token=token,
                               brand=brand)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #8
0
def logout():
    """
    Implements the logout provider component of the Hydra logout workflow.
    Hydra redirects to this endpoint based on the ``URLS_LOGOUT`` environment
    variable configured on the Hydra server.
    """
    try:
        challenge = request.args.get('logout_challenge')
        logout_request = hydra_admin.get_logout_request(challenge)
        redirect_to = hydra_admin.accept_logout_request(challenge)
        return redirect(redirect_to)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #9
0
def consent():
    """
    Implements the consent provider component of the Hydra consent workflow.
    Hydra redirects to this endpoint based on the ``URLS_CONSENT`` environment
    variable configured on the Hydra server.
    """
    challenge = request.args.get('consent_challenge')
    try:
        consent_request = hydra_admin.get_consent_request(challenge)
        user_id = consent_request['subject']
        client_id = consent_request['client']['client_id']
        try:
            user_permissions = get_user_permissions(user_id, client_id)
            user_info = get_user_info(user_id, client_id)

            grant_scope = [
                requested_scope_id
                for requested_scope_id in consent_request['requested_scope']
                if requested_scope_id in user_permissions
                or requested_scope_id not in ODPScope.__members__.values()
            ]
            consent_params = {
                'grant_scope':
                grant_scope,
                'grant_audience':
                consent_request['requested_access_token_audience'],
                'access_token_data':
                user_permissions,
                'id_token_data':
                asdict(user_info),
            }
            redirect_to = hydra_admin.accept_consent_request(
                challenge, **consent_params)
            return redirect(redirect_to)

        except x.ODPIdentityError as e:
            redirect_to = hydra_admin.reject_consent_request(
                challenge, e.error_code, e.error_description)
            return redirect(redirect_to)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #10
0
def verify():
    """View for sending a verification email.

    The token ensures that we can only get here from the user signup view.
    """
    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(token, 'signup.verify')

        form = VerifyEmailForm()
        email = params.get('email')
        name = params.get('name')

        if request.method == 'POST':
            send_verification_email(email, name, challenge, brand)

        return render_template('signup_verify.html', form=form, token=token, brand=brand)

    except x.HydraAdminError as e:
        return hydra_error_page(e)
Exemple #11
0
def login():
    """User login view.

    The token ensures that we can only access this view in the context
    of the Hydra login workflow.
    """
    token = request.args.get('token')
    try:
        login_request, challenge, brand, params = decode_token(token, 'login')

        user_id = None
        error = None
        form = LoginForm()

        if request.method == 'GET':
            authenticated = login_request['skip']  # indicates whether the user is already authenticated with Hydra

            # if already authenticated, we'll wind up with either a user_id or an error
            if authenticated:
                user_id = login_request['subject']
                try:
                    validate_auto_login(user_id)
                except x.ODPIdentityError as e:
                    # any validation error => reject login
                    user_id = None
                    error = e

            # if not authenticated, we'll display the login form

        else:  # POST
            if form.validate():
                email = form.email.data
                password = form.password.data
                try:
                    user_id = validate_user_login(email, password)

                except x.ODPUserNotFound:
                    form.email.errors.append("The email address is not associated with any user account.")

                except x.ODPNoPassword:
                    form.email.errors.append("Please click the 'Log in via Google' button.")

                except x.ODPIncorrectPassword:
                    form.email.errors.append("The email address and password do not match.")

                except x.ODPEmailNotVerified:
                    # the login is completed via email verification
                    name = get_user_profile_by_email(email)['name']
                    send_verification_email(email, name, challenge, brand)
                    verify_token = encode_token('login.verify', challenge, brand, email=email, name=name)
                    return redirect(url_for('.verify', token=verify_token))

                except x.ODPIdentityError as e:
                    # any other validation error (e.g. account locked/disabled) => reject login
                    error = e

        if user_id:
            redirect_to = hydra_admin.accept_login_request(challenge, user_id)
        elif error:
            redirect_to = hydra_admin.reject_login_request(challenge, error.error_code, error.error_description)
        else:
            return render_template('login.html', form=form, token=token, brand=brand, enable_google=config.GOOGLE.ENABLE)

        return redirect(redirect_to)

    except x.HydraAdminError as e:
        return hydra_error_page(e)