Search code examples
pythondockerpython-requestscontainersibm-cloud

IBM Bluemix authentication token always invalid


I've written a little Python script, which is supposed to access the IBM Bluemix container API described here using Python's requests module.

Here is the script:

"""
You can run this script for example as follows:

python test-script.py \
 --user <YOUR IBM BLUEMIX USER NAME> \
 --ca-certificate <PATH TO YOUR ca.pem> \
 --oauth-token "$(cf oauth-token)" \
 --space-id "$(cf space dev --guid)" \
 --request-url https://containers-api.ng.bluemix.net/v3/containers/version \
 --method GET

The request-url is an EXAMPLE!
"""

import argparse
import getpass
import requests
import json

# for parsing arguments provided on command line
parser = argparse.ArgumentParser()
parser.add_argument(
    '-u', '--user',
    required=True,
    help='Specify the username for IBM Bluemix.',
)
parser.add_argument(
    '-c', '--ca-certificate',
    required=True,
    help='Specify the location of the certificate of the trusted certification authority (ca.pem).'
)
parser.add_argument(
    '-t', '--oauth-token',
    required=True,
    help='Specify your IBM Bluemix oauth-token.'
)
parser.add_argument(
    '-s', '--space-id',
    required=True,
    help='Specify your IBM Bluemix space id (cf space <your space> --guid). Beware, the space needs to be available in the region you are logged in to (US South, United Kindom).'
)
parser.add_argument(
    '-l', '--request-url',
    required=True,
    default='https://containers-api.ng.bluemix.net/v3/containers/version',
    help='Specify the URL you want to send the request to.'
)
parser.add_argument(
    '-m', '--method',
    default='GET',
    help='Specify the HTTP method.'
)

args = parser.parse_args()

def run_script():
    password = getpass.getpass(prompt='IBM Bluemix password:')
    # for now hard coded
    headers = {
        'Accept': 'application/json',
        'X-Auth-Token': args.oauth_token,
        'X-Auth-Project-Id': args.space_id
    }

    # first we get the appropriate HTTP request method
    request_method = getattr(requests, args.method.lower())

    # then we try to make the request with that method
    try:
        response = request_method(
            args.request_url,
            auth=(args.user, password),
            data=None,  # expects a dict or json.dumps return type
            verify=args.ca_certificate,
            headers=headers
        )
        print(response)
        print(response.json())
    except Exception as exc:
        print('ERROR!')

def main():
    try:
        run_script()
    except KeyboardInterrupt as exc:
        exit('Interrupted, exiting ...')

main()

This script I call with:

python script.py \
 --user <IBM Bluemix login name> \
 --ca-certificate /home/<USER>/.ice/certs/containers-api.eu-gb.bluemix.net/<ID>/ca.pem \
 --oauth-token "$(cf oauth-token)" \
 --space-id "$(cf space dev --guid)" \
 --request-url https://containers-api.ng.bluemix.net/v3/images/json \
 --method GET

For the --user parameter I tried several things:

  • the first name plus last name from the IBM Bluemix dashboard page with a space in between as a string between two "
  • the complete email I used for registering, which is also labeled User: when I do cf login
  • the organization name, which is displayed when I do cf login

As for the --space-id and the authentication token --oauth-token: The documentation of the APi itself tells me to get them the way I do. I also tried supplying them via copy & paste of the output of the subcommands I used and also printed them from the script, just to be sure it all gets inside the script. It does.

The --request-url parameter value I also got from the API documentation, by using the "Try it" button and copying the URL from the curl command shown, which was used to trying, so that should also be correct.

However, in every request, which required authentication, for example the one for listing all images in the space, I get a 401 response from the API, telling me the authentication token was not valid and that I should try to do cf login and cf ic init again. Done that, doesn't change my token or anything and does not fix the error.

Here is an example response I get:

<Response [401]>
{'code': 'IC5097E', 
 'description': "Authentication was not successful: bearer <SOME ID> Please login via 'cf login ...' + 'cf ic init ...' and try again.", 
 'environment': 'prod-dal09',
 'host_id': '176',
 'incident_id': '<SOME ID>',
 'name': 'InvalidToken',
 'rc': '401',
 'type': 'Infrastructure'}

(I broke the lines, to make it more readable here on SO)

So I wonder what I am doing wrong with the token. How do I create the proper request with the authentication token?

Edit #1

I also decoded the JSON web token using https://jwt.io/ . It shows that the user name I entered is indeed the one the token contains as well.

Edit #2

I also checked (again) for the space to be available:

IBM Bluemix Dashboard spaces overview

As you can see there is a space named dev in both regions.


Solution

  • It looks like you are mixing UK and US regions - I see a cert for eu-gb.bluemix.net and the US API server. That might be the problem. I only have a space in the US, so I cannot test this, but...

    If you are working with a space in the UK, use the containers-api.eu-gb.bluemix.net cert and the UK region api server: https://containers-api.eu-gb.bluemix.net

    If you are working with a space in the US, use the cert for containers-api.ng.bluemix.net and use that API server.