Exemplo n.º 1
0
def fxa_login_url(config, state, next_path=None, action=None,
                  force_two_factor=False, request=None, id_token=None):
    if next_path and _is_safe_url(next_path, request):
        state += ':' + force_text(
            urlsafe_b64encode(next_path.encode('utf-8'))).rstrip('=')
    query = {
        'client_id': config['client_id'],
        'scope': 'profile openid',
        'state': state,
    }
    if action is not None:
        query['action'] = action
    if force_two_factor is True:
        # Specifying AAL2 will require the token to have an authentication
        # assurance level >= 2 which corresponds to requiring 2FA.
        query['acr_values'] = 'AAL2'
        # Requesting 'prompt=none' during authorization, together with passing
        # a valid id token in 'id_token_hint', allows the user to not have to
        # re-authenticate with FxA if they still have a valid session (which
        # they should here: they went through FxA, back to AMO, and now we're
        # redirecting them to FxA because we want them to have 2FA enabled).
        if id_token:
            query['prompt'] = 'none'
            query['id_token_hint'] = id_token
    if use_fake_fxa():
        base_url = reverse('fake-fxa-authorization')
    else:
        base_url = '{host}/authorization'.format(host=settings.FXA_OAUTH_HOST)
    return '{base_url}?{query}'.format(
        base_url=base_url, query=urlencode(query))
Exemplo n.º 2
0
def check_and_update_fxa_access_token(request):
    """This function checks access_token_expiry time in `request.session` and refreshes
    the access_token with the refresh token.

    IdentificationError from `get_fxa_token` will be raised if there is a problem
    refreshing, and `request.session` is updated with the new access_token_expiry time
    otherwise."""

    if (not use_fake_fxa() and settings.VERIFY_FXA_ACCESS_TOKEN and
        (request.session.get('fxa_access_token_expiry') or 0) < time.time()):
        if not request.session.get('fxa_refresh_token'):
            raise IdentificationError(
                'Could not get access token because refresh token missing from session'
            )

        config_name = (request.session['fxa_config_name']
                       if request.session.get('fxa_config_name')
                       in settings.ALLOWED_FXA_CONFIGS else
                       settings.DEFAULT_FXA_CONFIG_NAME)

        # This will raise IdentificationError if there is a problem
        token_data = get_fxa_token(
            refresh_token=request.session.get('fxa_refresh_token'),
            config=settings.FXA_CONFIG[config_name],
        )
        request.session['fxa_access_token_expiry'] = token_data[
            'access_token_expiry']
Exemplo n.º 3
0
def fake_fxa_authorization(request):
    """Fake authentication page to bypass FxA in local development envs."""
    if not use_fake_fxa():
        raise Http404()
    interesting_accounts = UserProfile.objects.exclude(groups=None).exclude(
        deleted=True)[:25]
    return render(request, 'amo/fake_fxa_authorization.html',
                  {'interesting_accounts': interesting_accounts})
Exemplo n.º 4
0
 def create_session(self, user, **overrides):
     """
     Creates a session in the database for this user and returns the session key.
     """
     request = HttpRequest()
     request.user = user
     # this is pretty much what django.contrib.auth.login does to initialize session
     fxa_details = ({
         'fxa_access_token_expiry': time.time() + 1000
     } if not use_fake_fxa() else {})
     initialize_session(
         request,
         {
             auth.SESSION_KEY: user._meta.pk.value_to_string(user),
             auth.BACKEND_SESSION_KEY: settings.AUTHENTICATION_BACKENDS[0],
             auth.HASH_SESSION_KEY: user.get_session_auth_hash(),
             **fxa_details,
             **overrides,
         },
     )
     return request.session.session_key
Exemplo n.º 5
0
        def inner(self, request):
            fxa_config = self.get_fxa_config(request)
            if request.method == 'GET':
                data = request.query_params
            else:
                data = request.data

            state_parts = data.get('state', '').split(':', 1)
            state = state_parts[0]
            next_path = parse_next_path(state_parts, request)
            if not data.get('code'):
                log.info('No code provided.')
                return render_error(request,
                                    ERROR_NO_CODE,
                                    next_path=next_path,
                                    format=format)
            elif (not request.session.get('fxa_state')
                  or request.session['fxa_state'] != state):
                log.info(
                    'State mismatch. URL: {url} Session: {session}'.format(
                        url=data.get('state'),
                        session=request.session.get('fxa_state'),
                    ))
                return render_error(request,
                                    ERROR_STATE_MISMATCH,
                                    next_path=next_path,
                                    format=format)
            elif request.user.is_authenticated:
                response = render_error(request,
                                        ERROR_AUTHENTICATED,
                                        next_path=next_path,
                                        format=format)
                # If the api token cookie is missing but we're still
                # authenticated using the session, add it back.
                if API_TOKEN_COOKIE not in request.COOKIES:
                    log.info(
                        'User %s was already authenticated but did not '
                        'have an API token cookie, adding one.',
                        request.user.pk,
                    )
                    response = add_api_token_to_response(
                        response, request.user)
                return response
            try:
                if use_fake_fxa() and 'fake_fxa_email' in data:
                    # Bypassing real authentication, we take the email provided
                    # and generate a random fxa id.
                    identity = {
                        'email':
                        data['fake_fxa_email'],
                        'uid':
                        'fake_fxa_id-%s' %
                        force_text(binascii.b2a_hex(os.urandom(16))),
                    }
                    id_token = identity['email']
                else:
                    identity, id_token = verify.fxa_identify(data['code'],
                                                             config=fxa_config)
            except verify.IdentificationError:
                log.info('Profile not found. Code: {}'.format(data['code']))
                return render_error(request,
                                    ERROR_NO_PROFILE,
                                    next_path=next_path,
                                    format=format)
            else:
                user = find_user(identity)
                # We can't use waffle.flag_is_active() wrapper, because
                # request.user isn't populated at this point (and we don't want
                # it to be).
                flag = waffle.get_waffle_flag_model().get(
                    '2fa-enforcement-for-developers-and-special-users')
                enforce_2fa_for_developers_and_special_users = flag.is_active(
                    request) or (flag.pk and flag.is_active_for_user(user))
                if (user and not identity.get('twoFactorAuthentication')
                        and enforce_2fa_for_developers_and_special_users
                        and (user.is_addon_developer or user.groups_list)):
                    # https://github.com/mozilla/addons/issues/732
                    # The user is an add-on developer (with other types of
                    # add-ons than just themes) or part of any group (so they
                    # are special in some way, may be an admin or a reviewer),
                    # but hasn't logged in with a second factor. Immediately
                    # redirect them to start the FxA flow again, this time
                    # requesting 2FA to be present - they should be
                    # automatically logged in FxA with the existing token, and
                    # should be prompted to create the second factor before
                    # coming back to AMO.
                    log.info('Redirecting user %s to enforce 2FA', user)
                    return HttpResponseRedirect(
                        fxa_login_url(
                            config=fxa_config,
                            state=request.session['fxa_state'],
                            next_path=next_path,
                            action='signin',
                            force_two_factor=True,
                            request=request,
                            id_token=id_token,
                        ))
                return fn(self,
                          request,
                          user=user,
                          identity=identity,
                          next_path=next_path)
Exemplo n.º 6
0
def fake_fxa_authorization(request):
    """Fake authentication page to bypass FxA in local development envs."""
    if not use_fake_fxa():
        raise Http404()
    return render(request, 'amo/fake_fxa_authorization.html')