403 Forbidden when using OAuth authorize endpoint for API_ONLY SmartApp

SmartThings OAuth-In SmartApp returns 403 Forbidden during authorization

I’m trying to create a SmartThings OAuth integration so MacroDroid can automatically refresh access tokens and control my garage door through the SmartThings API.

My goal is:

MacroDroid geofence (55m) → SmartThings API → Open garage door

I already have this working using a Personal Access Token (PAT), but PATs expire after 24 hours, so I’m trying to use OAuth refresh tokens instead.

What I have done:

  • Created an OAuth-In SmartApp using the SmartThings CLI.

  • App Type: API_ONLY

  • Classification: CONNECTED_SERVICE

  • Redirect URI: http://localhost

  • Generated OAuth Client ID and OAuth Client Secret successfully.

  • Verified the Redirect URI is saved correctly using: smartthings apps:oauth <app-id>

Current problem:

When I open:

https://api.smartthings.com/oauth/authorize?client_id=<CLIENT_ID>&response_type=code&redirect_uri=http://localhost

SmartThings immediately returns:

403 Forbidden - Access forbidden to this app

I also receive the same error when running:

smartthings apps:oauth:generate <app-id>

Things already checked:

  • Created a brand-new OAuth-In SmartApp from scratch.

  • Verified Redirect URI is exactly http://localhost.

  • Tried URL-encoded redirect URI.

  • Tried multiple OAuth Client IDs.

  • Same Samsung account owns the SmartThings devices and created the app.

  • App is visible via the SmartThings CLI.

  • OAuth Client ID and Secret are generated successfully.

Questions:

  1. Is there an additional configuration step required before an OAuth-In SmartApp can use the authorization code flow?

  2. Is http://localhost still supported as a redirect URI?

  3. Is there a different authorization endpoint for CONNECTED_SERVICE / API_ONLY applications?

  4. Why would both the authorization URL and apps:oauth:generate return 403 Forbidden - Access forbidden to this app?

Any guidance would be appreciated. My end goal is to obtain a refresh token so MacroDroid can automatically renew SmartThings access tokens without manual intervention.

Is anything actually listening on http://localhost?

The redirect URL is intended to be where your app is running, but if you are just after the code you can use pretty much any website that won’t redirect you somewhere else before you can read the URL. Of course it is impolite to just pick a random website. I used https://httpbin.org/get in a demo as that is made available for public use.

Hi, @Pravin81

We’ve seen other developers experience issues because they’re using a Localhost URL for the Redirect URI. So, we suggest you use a tool such as Ngrok to create a tunnel for the local port you open using an HTTPS URL.

If the issue is still happening after, we would need to check other details.

-Thanks for the information.

My use case is slightly different. I am not developing a SmartApp with a localhost redirect URI. I am using a Personal Access Token (PAT) to send SmartThings API commands from MacroDroid to a Meross MSG100 garage door opener.

Could you please clarify:

1. Are Personal Access Tokens intended to expire after 24 hours?

2. Is there a supported long-term authentication method for personal automations that does not require manually generating a new token every day?

3. For a non-SmartApp integration, would you recommend OAuth 2.0 with refresh tokens, or another approach?

My goal is a reliable long-term garage door automation that can trigger SmartThings API commands without requiring daily token replacement.

Thank you, that makes sense.

I was using “http://localhost” as the redirect URI because I was following the OAuth documentation examples and only needed to capture the authorization code manually.

Just to confirm, if I update my OAuth-In App redirect URI to a public HTTPS URL such as “https://httpbin.org/get”, should the standard authorization URL then allow me to obtain an authorization code and proceed with the authorization_code flow?

If so, I’ll recreate or update the OAuth configuration and test -again./.

Yes, this was applied in December 2024. Here’s the announcement about it:

Not for now; the only way to bypass the manual generation is by using the API_ONLY app and the OAuth flow where you can schedule a request to refresh the token.

Here I’ll share some steps that we provide when working with OAuth Integrations: