API authentication reference

The MAAS API uses OAuth 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). This article will help you learn:

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

How to perform authenticated requests in Python

Note: the below example uses fades, but you can also install the requests_oauthlib (pypi link) and oauthlib (pypi link) packages with pip. Replace <MAAS_SERVER_IP> with your server’s IP address, and <API-KEY> with your API key.

from oauthlib.oauth1 import SIGNATURE_PLAINTEXT # fades
from requests_oauthlib import OAuth1Session # fades

MAAS_HOST = "http://<MAAS_SERVER_IP>:5240/MAAS"
CONSUMER_KEY, CONSUMER_TOKEN, SECRET = "<API-KEY>".split(":")

maas = OAuth1Session(CONSUMER_KEY, resource_owner_key=CONSUMER_TOKEN, resource_owner_secret=SECRET, signature_method=SIGNATURE_PLAINTEXT)

nodes = maas.get(f"{MAAS_HOST}/api/2.0/machines/", params={"op": "list_allocated"})
nodes.raise_for_status()

print(nodes.json())

How to perform authenticated requests in 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:5240/MAAS/api/2.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:5240/MAAS/api/2.0", "/nodes/?op=list", "<key>", "<secret>",
     "consumer_key>")

How to perform authenticated requests using cURL

curl --header "Authorization: OAuth oauth_version=1.0, oauth_signature_method=PLAINTEXT, oauth_consumer_key=$API_KEY[1], oauth_token=$API_KEY[2], oauth_signature=&$API_KEY[3], oauth_nonce=$(uuidgen), oauth_timestamp=$(date +%s)" \
$MAAS_URL/MAAS/api/2.0/users/

How to perform authenticated requests using HTTPie and fish

set API_KEY (string split : $API_KEY)

http $MAAS_URL/api/2.0/users/ \
Authorization:"OAuth oauth_version=1.0, oauth_signature_method=PLAINTEXT, oauth_consumer_key=$API_KEY[1], oauth_token=$API_KEY[2], oauth_signature=&$API_KEY[3], oauth_nonce=$(uuidgen), oauth_timestamp=$(date +%s)"

This Python example does not seem to work in Python 3.

[nix-shell:~/maas-oauth]$ ./test.py
Traceback (most recent call last):
  File "/Users/jpparker/maas-oauth/./test.py", line 3, in <module>
    import oauth.oauth as oauth
  File "/nix/store/9bl1s62q3l3ndzci6681mh4v02gm6rnz-python3-3.9.10-env/lib/python3.9/site-packages/oauth/oauth.py", line 29, in <module>
    import urlparse
ModuleNotFoundError: No module named 'urlparse'

The “oauth” library used in the example last had a release in 2009. This was already reported (Working python oauth example) but it doesn’t look to have been fixed.

We updated this last year, it seems to ahve reverted. I will find out why.

@billwear for some reason, revision 8 was reverted. That revision had changes made by @sparkiegeek that moved away from the deprecated library and was in a working state. Can we please bring back that version? Not sure what happened.

In the meantime, this should work:

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>')

yep, accidental regression. will fix today.

should be done. lmk if it’s not right.

1 Like

You can also make an API call with cURL or HTTPie.
To do this you need to manually craft a proper Authorization header.

Here is an example using fish and HTTPie:

set API_KEY (string split : $API_KEY)

http $MAAS_URL/api/2.0/users/ \
Authorization:"OAuth oauth_version=1.0, oauth_signature_method=PLAINTEXT, oauth_consumer_key=$API_KEY[1], oauth_token=$API_KEY[2], oauth_signature=&$API_KEY[3], oauth_nonce=$(uuidgen), oauth_timestamp=$(date +%s)"

With cURL it will be as simple as:

curl --header "Authorization: OAuth oauth_version=1.0, oauth_signature_method=PLAINTEXT, oauth_consumer_key=$API_KEY[1], oauth_token=$API_KEY[2], oauth_signature=&$API_KEY[3], oauth_nonce=$(uuidgen), oauth_timestamp=$(date +%s)" \
$MAAS_URL/MAAS/api/2.0/users/
1 Like