API authentication

hMAAS’s API uses OAuth (external link) as its authentication mechanism. This isn’t third-party (3-legged) OAuth, so the process used is what’s commonly referred to as 0-legged OAuth: the consumer accesses protected resources by submitting OAuth signed requests.

Note that some API endpoints support unauthenticated requests (i.e. anonymous access). See the API documentation for details.

Two questions you may have:

  1. How can I perform authenticated requests in python?
  2. How can I perform authenticated requests in ruby?

Here are two examples on how to perform an authenticated GET request to retrieve the list of nodes. The <key>, <secret>, <consumer_key> tokens are the three elements that compose the API key (API key = ‘<consumer_key>:<key>:<secret>’).

Python

import oauth.oauth as oauth
import httplib2
import uuid

def perform_API_request(site, uri, method, key, secret, consumer_key):
    resource_tok_string = "oauth_token_secret=%s&oauth_token=%s" % (
        secret, key)
    resource_token = oauth.OAuthToken.from_string(resource_tok_string)
    consumer_token = oauth.OAuthConsumer(consumer_key, "")

    oauth_request = oauth.OAuthRequest.from_consumer_and_token(
        consumer_token, token=resource_token, http_url=site,
        parameters={'oauth_nonce': uuid.uuid4().hex})
    oauth_request.sign_request(
        oauth.OAuthSignatureMethod_PLAINTEXT(), consumer_token,
        resource_token)
    headers = oauth_request.to_header()
    url = "%s%s" % (site, uri)
    http = httplib2.Http()
    return http.request(url, method, body=None, headers=headers)

# API key = '<consumer_key>:<key>:<secret>'
response = perform_API_request(
    'http://server/MAAS/api/1.0', '/nodes/?op=list', 'GET', '<key>', '<secret>',
    '<consumer_key>')

Ruby

require 'oauth'
require 'oauth/signature/plaintext'

def perform_API_request(site, uri, key, secret, consumer_key)
    consumer = OAuth::Consumer.new(
        consumer_key, "",
        { :site => "http://localhost/MAAS/api/1.0",
          :scheme => :header, :signature_method => "PLAINTEXT"})
    access_token = OAuth::AccessToken.new(consumer, key, secret)
    return access_token.request(:get, "/nodes/?op=list")
end

# API key = "<consumer_key>:<key>:<secret>"
response = perform_API_request(
     "http://server/MAAS/api/1.0", "/nodes/?op=list", "<key>", "<secret>",
     "consumer_key>")

The codeblock provided while I’m no python expert, I couldn’t resolve those libs easily. I found this to run without extra work from a maas deployment of 20.04 without anything extra other than requiring requests_oauthlib. I think this could be re-written to only use oauthlib which if I recall is built in. Either way, the code sample there helped me arrive here, but I find this to be more readable to understanding the oauth flow, and how it might relate to other oAuth flows.
import argparse
from sys import argv
import requests
from requests_oauthlib import OAuth1

def oauth_request(site, uri, key, secret, consumer_key):
    headeroauth = OAuth1(key,
        client_secret='',
        resource_owner_key=secret,
        resource_owner_secret=consumer_key,
        signature_method='PLAINTEXT')
    r = requests.get(f'{site}{uri}', auth=headeroauth)
    print(r.text)
    return r

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--server', help = "Maas server.  ex. http://maas:5240/MAAS ")
    parser.add_argument('--key', help = "Maas key.  ex. ABCD:abcd:abcd ")
    parser.add_argument('--secret', help = "Maas secret.  ex. abcd:ABCD:abcd ")
    parser.add_argument('--consumer', help = "Maas consumer key.  ex. abcd:abcd:ABCD ")
    args = parser.parse_args(argv[1:])
    # API key = '<consumer_key>:<key>:<secret>'
    response = oauth_request(
        f'{args.server}/api/2.0', '/pods/',
        args.key, args.secret, args.consumer)
    print(response)

I tried the code with a valid API key and it returned “Invalid API key”.

I removed the parser portion but am running into a problem.

def oauth_request(site, uri, key, secret, consumer_key):
headeroauth = OAuth1(key,
client_secret=’’,
resource_owner_key=secret,
resource_owner_secret=consumer_key,
signature_method=‘PLAINTEXT’)
r = requests.get(f’{site}{uri}’, auth=headeroauth)
print(r.text)
return r

#API key = ‘<consumer_key>::’
consumer_key, key, secret = MAASAPIKEY.split(’:’)
response = oauth_request( f’{MAASURL}/api/2.0’, ‘/machines/’, key, secret, consumer_key)
print(response)

Any ideas why I’d get the error?