OAuth implementation

I’m trying to integrate Square’s OAuth flow into my application using the nodeJS SDK. I can perform a handshake with a client and receive authorization to act on their behalf whereas I receive the access and refresh tokens, and I can revoke the entire authorization based on the merchantId I receive, but those two things are really all I’ve been able to figure out.

The access token will expire in 30 days, and while the docs tell me that I need to refresh it (every 7 days), they only mention how to migrate from ‘renew’ to ‘refresh’. I speculate that the obtainToken endpoint may be what’s needed with a grantType of ‘refresh_token’, but I’m only guessing and the docs do not answer this. When I try to refresh my current token (2 days old) it simply gets returned with the current expiration date. I need to understand how to refresh these tokens so that the clients don’t have to forcibly reauthenticate my app every 30 days. If I have to wait a week to test a token refresh, that’s going to be a real problem for development.

Also, I’m embedding Square’s payment form JS into my app (having to pass my Square App Id to the client) and am properly receiving a nonce from the form. I did this on my own as again there are no examples to demonstrate it in Angular (if anyone wants to see what I did, I’ll be glad to share). However, I can do nothing with it as while the nonce is supposed to be provided as ‘sourceId’ to the createPayment method, that method doesn’t accept the access token I’ve been working hard to capture. So how can I turn this nonce into a charge for the client I am synchronized with? I’m trying to facilitate charges for my clients using Square’s OAuth flow, but I am feeling as if I’m really missing something.

With OAuth to refresh the token you want to call ObtainToken with the refresh token:

curl https://connect.squareup.com/oauth2/token \
  -X POST \
  -H 'Square-Version: 2022-04-20' \
  -H 'Content-Type: application/json' \
  -d '{
    "client_id": "<YOUR_APPLICATION_ID>",
    "grant_type": "refresh_token",
    "redirect_uri": "<THE REDIRECT URL>",
    "client_secret": "<YOUR_APPLICATION_SECRET>",
    "refresh_token": "<CURRENT REFRESH TOKEN>"

You mentioned the Square Payment form for processing payment and nonce generation. Are you using the deprecated SqPaymentForm or are you using our fully supported Web Payments SDK? The SqPaymentForm will be retired in October which means that it will no longer work for your application. :slightly_smiling_face:

If I have an application that is acting as a gateway for multiple clients, how can I use the payload you are showing to refresh a client’s token? Looking at the payload you supplied:

client_id is my app id. makes sense.
grant_type of ‘refresh_token’ makes sense.
redirect_uri makes no sense at all - this is happening in the api layer. My backend needs to hear this and not be redirected.
client_secret is my client secret token. makes sense.
refresh_token makes sense, but does your api automatically recognize the merchantId from it? shouldn’t I be sending that? how can i tell your api that this is for a specific customer I’m authorized to work with?

If my payload is correct, I need to be able to legitimately test refreshing a token without having to wait a week. How can I prove that it’s working? I can’t go to production until I can verify a proper response from your endpoint.

I’m calling one of these scripts on demand depending on the environment: https://web.squarecdn.com/v1/square.js / https://sandbox.web.squarecdn.com/v1/square.js

Looks to be your current Web Payments SDK. Since the nonce is generated only with my app id and the payments API only only takes my app id and the source id (nonce), I’m wondering how I am supposed to inform the payments API that this charge is for a certain client.

Is the answer to submit the client access token when instantiating the Client class?

new Client({
    accessToken: [client token],
    environment: 'sandbox' / 'production'

The redirect_uri is the redirect URL assigned in the OAuth page for your application for the initial OAuth process. Sorry for including it in the example. It’s not required. With the access tokens they’re scoped to a specific account so there’s no need to pass in the merchant_id when refreshing the token. Also the refresh_token will be specific to the merchants account.

For testing you can use sandbox for testing a refresh_token and refresh the token in any timeframe. There’s not need to wait a week to refresh a token.

Lastly, when processing payments the Web Payments SDK will generate the token which you pass into the CreatePayment request as the source_id. The CreatePayment request is the server side request that uses the access token in the header of the request. It doesn’t use the app ID. :slightly_smiling_face:

I have one final question before going to production. The docs recommend verifying the token via calling the listLocations method. Should I be verifying the existence of at least one location, or just verify the absence of errors?

And I have another concern as I’m now testing in production before fully launching - I just attempted to authorize my prod Square account with the application, and it instantly connected without asking if I wanted to accept. This is scary - is this behavior automatic because my account is a sibling of the application requesting authorization? Even if so, this should not be permitted. I need to know that my clients won’t have this happen and will get the chance to read my request params and opt out at this stage.

When calling ListLocations with the newly generated token your verifying for a 200 response. If there’s anything else in the payload of the response that you want to validate like CREDIT_CARD_PROCESSING in capabilities you can do that too.

When it’s automatically authorizing you are you logged in? Also is the session set to false? :slightly_smiling_face:

I’m not passing in session, so it defaults to true. This is irrelevant. Whether your customer is forced to log into your platform again has no bearing on whether they are automatically forced to accept whatever rights I request from them without their being able to first view and agree.

If this is happening because the account is attached to my application, then this is fine. If it’s happening for other reasons, then this is a critical and dangerous issue that your platform must address immediately.

It’s happening cause you’re already authorized and there are no new permission scopes that are being requested so there’s nothing to new to authorize. :slightly_smiling_face:

Excellent. Thanks for looking into this.