Example #1
0
    def test_refresh_token_http_basic(self):

        user_1 = User(username='******')
        user_1.set_password('12345')
        user_1.save()

        client_1 = Client(user=user_1, title='OClient', identifier='OClient', password='******')
        client_1.save()

        client_2 = Client(user=user_1, title='OGOClient', identifier='OGOClient', password='******')
        client_2.save()

        redirect_1 = RedirectionEndpoint(client=client_1, uri='http://redirect-test.com')
        redirect_1.save()

        token_1 = Token(client=client_1, user=user_1)
        token_1.save()

        token_2 = Token(client=client_2, user=user_1)
        token_2.save()

        date_issued = token_1.date_issued
        access_token = token_1.access_token
        refresh_token = token_1.refresh_token

        refresh_token_wrong_client = token_2.refresh_token

        # Missing required params.
        resp = self.client.post(URL_TOKEN, {'grant_type': 'refresh_token'},
                                Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp.content_json['error'], 'invalid_request')

        # Invalid refresh token supplied.
        resp = self.client.post(URL_TOKEN, {'grant_type': 'refresh_token', 'refresh_token': 'invalid'},
                                Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp.content_json['error'], 'invalid_grant')

        # Refresh token from another client is supplied.
        resp = self.client.post(URL_TOKEN, {'grant_type': 'refresh_token', 'refresh_token': refresh_token_wrong_client},
                                Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp.content_json['error'], 'invalid_grant')

        # Valid request.
        resp = self.client.post(URL_TOKEN, {'grant_type': 'refresh_token', 'refresh_token': refresh_token},
                                Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')

        self.assertEqual(resp.status_code, 200)
        self.assertTrue('access_token' in resp.content_json)
        self.assertTrue('refresh_token' in resp.content_json)
        self.assertTrue('token_type' in resp.content_json)
        self.assertTrue('expires_in' not in resp.content_json)

        self.assertNotEqual(access_token, resp.content_json['access_token'])
        self.assertNotEqual(refresh_token, resp.content_json['refresh_token'])

        token_updated = Token.objects.get(access_token=resp.content_json['access_token'])
        self.assertNotEqual(date_issued, token_updated.date_issued)
Example #2
0
    def test_refresh_token_http_basic(self, settings, client, user):

        settings.DEBUG = True

        client_1 = Client(user=user, title='OClient', identifier='OClient', password='******')
        client_1.save()

        client_2 = Client(user=user, title='OGOClient', identifier='OGOClient', password='******')
        client_2.save()

        redirect_1 = RedirectionEndpoint(client=client_1, uri='http://redirect-test.com')
        redirect_1.save()

        token_1 = Token(client=client_1, user=user)
        token_1.save()

        token_2 = Token(client=client_2, user=user)
        token_2.save()

        date_issued = token_1.date_issued
        access_token = token_1.access_token
        refresh_token = token_1.refresh_token

        refresh_token_wrong_client = token_2.refresh_token

        # Missing required params.
        resp = client.post(
            URL_TOKEN, {'grant_type': 'refresh_token'},
            Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')

        assert resp.status_code == 400
        assert resp.content_json['error'] == 'invalid_request'

        # Invalid refresh token supplied.
        resp = client.post(
            URL_TOKEN, {'grant_type': 'refresh_token', 'refresh_token': 'invalid'},
            Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')

        assert resp.status_code == 400
        assert resp.content_json['error'] == 'invalid_grant'

        # Refresh token from another client is supplied.
        resp = client.post(
            URL_TOKEN, {'grant_type': 'refresh_token', 'refresh_token': refresh_token_wrong_client},
            Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')

        assert resp.status_code == 400
        assert resp.content_json['error'] == 'invalid_grant'

        # Valid request.
        resp = client.post(
            URL_TOKEN, {'grant_type': 'refresh_token', 'refresh_token': refresh_token},
            Authorization='Basic T0NsaWVudDpjbDAxMjM0NQ==')

        assert resp.status_code == 200
        assert 'access_token' in resp.content_json
        assert 'refresh_token' in resp.content_json
        assert 'token_type' in resp.content_json
        assert 'expires_in' not in resp.content_json

        assert access_token != resp.content_json['access_token']
        assert refresh_token != resp.content_json['refresh_token']

        token_updated = Token.objects.get(access_token=resp.content_json['access_token'])
        assert date_issued != token_updated.date_issued
Example #3
0
def endpoint_token(request):
    """
    SPEC: The token endpoint is used by the client to obtain an access
    token by presenting its authorization grant or refresh token.  The token
    endpoint is used with every authorization grant except for the
    implicit grant type (since an access token is issued directly).

    SPEC: The authorization server MUST support TLS...
    The client MUST use the HTTP "POST" method when making access token requests.

    """

    # SPEC: Since requests to the token endpoint result in the transmission
    # of clear-text credentials (in the HTTP request and response),
    # the authorization server MUST require the use of a transport-layer
    # security mechanism when sending requests to the token endpoint.
    if not request.is_secure() and not settings.DEBUG:
        # Insecure connections are only available in debug mode.
        return ep_auth_response_error_page(
            request, _('OAuth requires secure connection to be established.'),
            403)

    input_params = filter_input_params(request.POST)

    grant_type = input_params.get('grant_type')
    if grant_type not in REGISTRY_EP_TOKEN_GRANT_TYPE:
        return ep_token_reponse_error(
            'unsupported_grant_type',
            'Usupported grant type is requested. Expected: `%s`. Given: `%s`' %
            ('`, `'.join(REGISTRY_EP_TOKEN_GRANT_TYPE), grant_type))

    token_obj_params = {}
    error_out_headers = {}
    client = None
    client_id = None
    client_secret = None

    # TODO More client authentication methods implementations needed.
    authorization_method = request.META.get('Authorization')
    if authorization_method is not None:
        # Authorization header detected.
        auth_method_type, auth_method_value = authorization_method.split(
            ' ', 1)
        error_out_headers['WWW-Authenticate'] = auth_method_type
        # Handle client auth through HTTP Basic.
        if auth_method_type == 'Basic':
            try:
                client_id, client_secret = base64.b64decode(
                    auth_method_value).split(':')
            except Exception:
                pass
    else:
        # SPEC: Including the client credentials in the request body using the two
        # parameters is NOT RECOMMENDED, and should be limited to clients
        # unable to directly utilize the HTTP Basic authentication scheme (or other
        # password-based HTTP authentication schemes).
        client_id = input_params.get('client_id')
        client_secret = input_params.get('client_secret')

    if client_id is not None:
        try:
            client = Client.objects.get(identifier=client_id)
        except ObjectDoesNotExist:
            client = None

        # SPEC: A public client that was not issued a client password MAY use
        # the "client_id" request parameter to identify itself when sending requests
        # to the token endpoint.
        if client is not None:
            if client.password.strip(
            ) != '' and client.password != client_secret:
                client = None

    if client is None:
        return ep_token_reponse_error(
            'invalid_client',
            'Unable to authenticate client by its credentials.', 401,
            error_out_headers)

    # Calculate token expiration datetime.
    expires_in = client.token_lifetime
    expires_at = None
    if expires_in is not None:
        expires_at = datetime.fromtimestamp(int(time() + expires_in))

    # TODO Scopes handling implementation required.

    if grant_type == 'authorization_code':  # Grant Type: Authorization code.
        code = input_params.get('code')
        redirect_uri = input_params.get('redirect_uri')

        if code is None or redirect_uri is None:
            return ep_token_reponse_error(
                'invalid_request',
                'Required param(s) are missing. Expected: `code` and `redirect_uri`.'
            )

        try:
            code = AuthorizationCode.objects.get(code=code)
        except ObjectDoesNotExist:
            return ep_token_reponse_error(
                'invalid_grant', 'Invalid authorization code is supplied.')

        # SPEC: If an authorization code is used more than once, the authorization
        # server MUST deny the request and SHOULD attempt to revoke all tokens
        # previously issued based on that authorization code.
        previous_tokens = Token.objects.filter(code=code).all()
        if len(previous_tokens) > 0:
            previous_tokens.delete()
            code.delete()
            return ep_token_reponse_error(
                'invalid_grant',
                'Authorization code is used more than once. Code and tokens are revoked.'
            )

        if code.uri != redirect_uri:
            return ep_token_reponse_error(
                'invalid_grant',
                'Supplied URI does not match the URI associated with authorization code.'
            )

        if code.client.id != client.id:
            return ep_token_reponse_error(
                'invalid_grant',
                'Authorization code supplied was issued to another client.')

        user = code.user
        token_obj_params['code'] = code

    elif grant_type == 'password':  # Grant type: Resource Owner Password Credentials.
        username = input_params.get('username')
        password = input_params.get('password')

        if username is None or password is None:
            return ep_token_reponse_error(
                'invalid_request',
                'Required param(s) are missing. Expected: `username` and `password`.'
            )

        invalid_credentials = False
        try:
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            invalid_credentials = True
        else:
            if not user.check_password(password):
                invalid_credentials = True

        if invalid_credentials:
            return ep_token_reponse_error(
                'invalid_grant',
                'Supplied resource owner credentials are invalid.')

    elif grant_type == 'client_credentials':  # Grant type: Client Credentials.
        # That one is somewhat unclear.
        # So let's suppose that the user is one, who has registered the client.
        user = client.user

    elif grant_type == 'refresh_token':  # Refreshing an Access Token.
        refresh_token = input_params.get('refresh_token')

        if refresh_token is None:
            return ep_token_reponse_error(
                'invalid_request',
                'Required `refresh_token` param is missing.')

        try:
            token = Token.objects.get(refresh_token=refresh_token)
        except ObjectDoesNotExist:
            return ep_token_reponse_error(
                'invalid_grant', 'Refresh token supplied is invalid.')
        else:
            if token.client_id != client.id:
                return ep_token_reponse_error(
                    'invalid_grant',
                    'Refresh token supplied was issued to another client.')

        # For refresh token grant we only swap token values.
        token.date_issued = datetime.now()
        token.access_token = token.generate_token()
        token.refresh_token = token.generate_token()

    if grant_type != 'refresh_token':
        token = Token(client=client,
                      user=user,
                      expires_at=expires_at,
                      **token_obj_params)

    token.save()

    output_params = {
        'access_token': token.access_token,
        'token_type': token.access_token_type,
        'refresh_token': token.refresh_token
    }

    if expires_in is not None:
        output_params['expires_in'] = expires_in

    if grant_type == 'client_credentials':
        del (output_params['refresh_token'])

    # TODO Some auth methods require additional parameters to be passed as spec puts it.
    additional_params = {}

    return ep_token_response(output_params)
Example #4
0
def endpoint_token(request):
    """
    SPEC: The token endpoint is used by the client to obtain an access
    token by presenting its authorization grant or refresh token.  The token
    endpoint is used with every authorization grant except for the
    implicit grant type (since an access token is issued directly).

    SPEC: The authorization server MUST support TLS...
    The client MUST use the HTTP "POST" method when making access token requests.

    """

    # SPEC: Since requests to the token endpoint result in the transmission
    # of clear-text credentials (in the HTTP request and response),
    # the authorization server MUST require the use of a transport-layer
    # security mechanism when sending requests to the token endpoint.
    if not request.is_secure() and not settings.DEBUG:
        # Insecure connections are only available in debug mode.
        return ep_auth_response_error_page(request, _('OAuth requires secure connection to be established.'), 403)

    input_params = filter_input_params(request.POST)

    grant_type = input_params.get('grant_type')
    if grant_type not in REGISTRY_EP_TOKEN_GRANT_TYPE:
        return ep_token_reponse_error('unsupported_grant_type', 'Usupported grant type is requested. Expected: `%s`. Given: `%s`' % ('`, `'.join(REGISTRY_EP_TOKEN_GRANT_TYPE), grant_type))

    token_obj_params = {}
    error_out_headers = {}
    client = None
    client_id = None
    client_secret = None

    # TODO More client authentication methods implementations needed.
    authorization_method = request.META.get('Authorization')
    if authorization_method is not None:
        # Authorization header detected.
        auth_method_type, auth_method_value = authorization_method.split(' ', 1)
        error_out_headers['WWW-Authenticate'] = auth_method_type
        # Handle client auth through HTTP Basic.
        if auth_method_type == 'Basic':
            try:
                client_id, client_secret = base64.b64decode(auth_method_value).split(':')
            except Exception:
                pass
    else:
        # SPEC: Including the client credentials in the request body using the two
        # parameters is NOT RECOMMENDED, and should be limited to clients
        # unable to directly utilize the HTTP Basic authentication scheme (or other
        # password-based HTTP authentication schemes).
        client_id = input_params.get('client_id')
        client_secret = input_params.get('client_secret')

    if client_id is not None:
        try:
            client = Client.objects.get(identifier=client_id)
        except ObjectDoesNotExist:
            client = None

        # SPEC: A public client that was not issued a client password MAY use
        # the "client_id" request parameter to identify itself when sending requests
        # to the token endpoint.
        if client is not None:
            if client.password.strip() != '' and client.password != client_secret:
                client = None

    if client is None:
        return ep_token_reponse_error('invalid_client', 'Unable to authenticate client by its credentials.', 401, error_out_headers)

    # Calculate token expiration datetime.
    expires_in = client.token_lifetime
    expires_at = None
    if expires_in is not None:
        expires_at = datetime.fromtimestamp(int(time() + expires_in))

    # TODO Scopes handling implementation required.

    if grant_type == 'authorization_code':  # Grant Type: Authorization code.
        code = input_params.get('code')
        redirect_uri = input_params.get('redirect_uri')

        if code is None or redirect_uri is None:
            return ep_token_reponse_error('invalid_request', 'Required param(s) are missing. Expected: `code` and `redirect_uri`.')

        try:
            code = AuthorizationCode.objects.get(code=code)
        except ObjectDoesNotExist:
            return ep_token_reponse_error('invalid_grant', 'Invalid authorization code is supplied.')

        # SPEC: If an authorization code is used more than once, the authorization
        # server MUST deny the request and SHOULD attempt to revoke all tokens
        # previously issued based on that authorization code.
        previous_tokens = Token.objects.filter(code=code).all()
        if len(previous_tokens) > 0:
            previous_tokens.delete()
            code.delete()
            return ep_token_reponse_error('invalid_grant', 'Authorization code is used more than once. Code and tokens are revoked.')

        if code.uri != redirect_uri:
            return ep_token_reponse_error('invalid_grant', 'Supplied URI does not match the URI associated with authorization code.')

        if code.client.id != client.id:
            return ep_token_reponse_error('invalid_grant', 'Authorization code supplied was issued to another client.')

        user = code.user
        token_obj_params['code'] = code

    elif grant_type == 'password':  # Grant type: Resource Owner Password Credentials.
        username = input_params.get('username')
        password = input_params.get('password')

        if username is None or password is None:
            return ep_token_reponse_error('invalid_request', 'Required param(s) are missing. Expected: `username` and `password`.')

        invalid_credentials = False
        try:
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            invalid_credentials = True
        else:
            if not user.check_password(password):
                invalid_credentials = True

        if invalid_credentials:
            return ep_token_reponse_error('invalid_grant', 'Supplied resource owner credentials are invalid.')

    elif grant_type == 'client_credentials':  # Grant type: Client Credentials.
        # That one is somewhat unclear.
        # So let's suppose that the user is one, who has registered the client.
        user = client.user

    elif grant_type == 'refresh_token':  # Refreshing an Access Token.
        refresh_token = input_params.get('refresh_token')

        if refresh_token is None:
            return ep_token_reponse_error('invalid_request', 'Required `refresh_token` param is missing.')

        try:
            token = Token.objects.get(refresh_token=refresh_token)
        except ObjectDoesNotExist:
            return ep_token_reponse_error('invalid_grant', 'Refresh token supplied is invalid.')
        else:
            if token.client_id != client.id:
                return ep_token_reponse_error('invalid_grant', 'Refresh token supplied was issued to another client.')

        # For refresh token grant we only swap token values.
        token.date_issued = datetime.now()
        token.access_token = token.generate_token()
        token.refresh_token = token.generate_token()

    if grant_type != 'refresh_token':
        token = Token(client=client, user=user, expires_at=expires_at, **token_obj_params)

    token.save()

    output_params = {
        'access_token': token.access_token,
        'token_type': token.access_token_type,
        'refresh_token': token.refresh_token
    }

    if expires_in is not None:
        output_params['expires_in'] = expires_in

    if grant_type == 'client_credentials':
        del(output_params['refresh_token'])

    # TODO Some auth methods require additional parameters to be passed as spec puts it.
    additional_params = {}

    return ep_token_response(output_params)