Search code examples
google-app-enginegoogle-apps-scriptoauth-2.0google-oauth

Connecting to bank account with OAuth2.0 and Google App Scripts


My bank offers to connect to their APIs using OAuth2. I want to create an App Script to autmatically write my transactions to google docs. This is my first time using OAuth and I can't seem to get the access token. Below I have pasted my attempt at receiving an access token, but the site refuses my request. Below my code example are two code examples (one for Node and one for Python) which are provided by the bank. Are there any obvious mistakes that I don't see?

My code:

function get_auth_token() {
    var identityServerUrl = "https://auth.sbanken.no/identityserver/connect/token"; // access token endpoint

     var clientId = '...'
     var secret = '...'

    var basicAuth = Utilities.base64Encode(encodeURIComponent(clientId) + ":" + encodeURIComponent(secret)); // create basicAuth header value according to Oauth 2.0 standard

    var response = UrlFetchApp.fetch(identityServerUrl, {
      headers: {
        'Accept': 'application/json',
        'customerId' : clientId,
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Basic ' + basicAuth}
    });
}

Node code from the bank:

exports.getAccessToken = () => {
    var identityServerUrl = "https://auth.sbanken.no/identityserver/connect/token"; // access token 
 endpoint

    var clientId = credentials.clientid; // application key received from API Beta in the internetbank
    var secret = credentials.secret; // password received from API Beta in the internetbank

    var basicAuth = btoa(encodeURIComponent(clientId) + ":" + encodeURIComponent(secret)); // create basicAuth header value according to Oauth 2.0 standard

    var accessToken;

    // request accessToken (the basic auth data is put on the request header prior to sending the request)

    let response;

    var promise = new Promise(function (resolve, reject) {
        request
        .post(identityServerUrl)
        .send('grant_type=client_credentials')
        .set('Authorization',  "Basic "+basicAuth)
        .set('Accept', 'application/json')
        .set('customerId', credentials.userid)
        .end(function(err, res){
          if (err || !res.ok) {
            console.log(err);
            reject();
          } else {
            console.log('yay got ' + JSON.stringify(res.body));
            resolve(res.body);
          }
        });
    });

Python code from the bank:

CLIENT_ID = ''  # Get from https://secure.sbanken.no/Personal/ApiBeta/Info/
SECRET = ''  # Get this from https://secure.sbanken.no/Personal/ApiBeta/Info/    
AUTH_URL = 'https://auth.sbanken.no/identityserver/connect/token'
ACCOUNTS_URL = 'https://api.sbanken.no/exec.bank/api/v1/accounts'
CUSTOMER_ID = ''  # Your own personnummer


def get_auth_token(auth_url, client_id, secret):
    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    body = {'grant_type': 'client_credentials'}

    urlencoded_client_id = urllib.quote(client_id)
    urlencoded_secret = urllib.quote(secret)
    auth_string_to_be_b64encoded = '{}:{}'.format(
        urlencoded_client_id, urlencoded_secret)
    b64encoded_auth_string = base64.b64encode(auth_string_to_be_b64encoded)
    headers['Authorization'] = 'Basic {}'.format(b64encoded_auth_string)

    r = requests.post(url=auth_url, headers=headers, data=body)
    auth_token = r.json()['access_token']
    return auth_token

Solution

  • From the code samples, I'd bet that you need to pass grant_type=client_credentials as part of the request. There are two ways you can do this:

    • Add it to the URL https://auth.sbanken.no/identityserver/connect/token?grant_type=client_credentials
    • Include it as the payload of the UrlFetchApp.fetch() request (this would be the more "correct" approach):

      var response = UrlFetchApp.fetch(identityServerUrl, {
        headers: {
          'Accept': 'application/json',
          'customerId' : clientId,
          'Content-Type': 'application/x-www-form-urlencoded',
          'Authorization': 'Basic ' + basicAuth
        },
        payload: 'grant_type=client_credential'
      });
      

    Unfortunately, I can't verify if that's all you'll need to do to make it work, but it should get you going in the right direction. Also, I'd really recommend you try to configure this through Google's OAuth2 library, although it isn't the most straight-forward to implement.