OpenWonderLabs / SwitchBotAPI

SwitchBot Open API Documents
843 stars 70 forks source link

Authentication not possible because of missing Request Header Keys #336

Closed jmo5k02 closed 2 weeks ago

jmo5k02 commented 3 weeks ago

Current Situation

I was trying to make some API requests to the SwitchBotAPIv1.1, because I own some SwitchBot products. For this I used the official python 3 example However I got an error code 403 when hitting 'https://api.switch-bot.com/v1.1/'

Logs

Authorization: xxx
t: 1724270723080
sign: xxx
nonce: 881ca366-5973-492d-9cc0-8638ae8f1113
Errorcode: 403
Response: {'message': "Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header. Authorization=xxx"}

Configuration

import json
import time
import hashlib
import hmac
import base64
import uuid
import requests

# Declare empty header dictionary
apiHeader = {}

token = 'tokenstring'
secret = 'secretstring'

nonce = uuid.uuid4()
t = int(round(time.time() * 1000))
string_to_sign = '{}{}{}'.format(token, t, nonce)

string_to_sign = bytes(string_to_sign, 'utf-8')
secret = bytes(secret, 'utf-8')

sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())
print ('Authorization: {}'.format(token))
print ('t: {}'.format(t))
print ('sign: {}'.format(str(sign, 'utf-8')))
print ('nonce: {}'.format(nonce))

#Build api header JSON
apiHeader['Authorization']=token
apiHeader['Content-Type']='application/json'
apiHeader['charset']='utf8'
apiHeader['t']=str(t)
apiHeader['sign']=str(sign, 'utf-8')
apiHeader['nonce']=str(nonce)

url = 'https://api.switch-bot.com/v1.1/'

# Send the request (GET in this example)
response = requests.get(url, headers=apiHeader)

# Print the response
print(response.status_code)
print(response.json())

Environment

Additional Context

No response

hsakoh commented 3 weeks ago

You might be confused due to the incorrect endpoint responding with an authentication error, but the issue is simply that the API URL you're calling is incorrect. (Line 37)

If you are enumerating devices, the correct URL should be: url = 'https://api.switch-bot.com/v1.1/devices'. If you are retrieving a specific device's status, the correct URL should be: url = 'https://api.switch-bot.com/v1.1/devices/AABBCCDDEEFF/status'.

jmo5k02 commented 3 weeks ago

I also tried that.

hitting url = 'https://api.switch-bot.com/v1.1/devices' gives me the following error:

Errorcode: 401 Response: {'message': 'Unauthorized'}

hsakoh commented 3 weeks ago

Could you be entering the token and secret in reverse? The token is 96 characters long. The secret is 32 characters long.

jmo5k02 commented 3 weeks ago

Sorry for my inexperience What do you mean with in reverse?

hsakoh commented 3 weeks ago

Could you be entering the secret in the token field and the token in the secret field by mistake? In my environment, the following code get a 200 {'statusCode': 100, 'body':...} response.

import json
import time
import hashlib
import hmac
import base64
import uuid
import requests

# Declare empty header dictionary
apiHeader = {}

token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

nonce = uuid.uuid4()
t = int(round(time.time() * 1000))
string_to_sign = '{}{}{}'.format(token, t, nonce)

string_to_sign = bytes(string_to_sign, 'utf-8')
secret = bytes(secret, 'utf-8')

sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())
print ('Authorization: {}'.format(token))
print ('t: {}'.format(t))
print ('sign: {}'.format(str(sign, 'utf-8')))
print ('nonce: {}'.format(nonce))

#Build api header JSON
apiHeader['Authorization']=token
apiHeader['Content-Type']='application/json'
apiHeader['charset']='utf8'
apiHeader['t']=str(t)
apiHeader['sign']=str(sign, 'utf-8')
apiHeader['nonce']=str(nonce)

url = 'https://api.switch-bot.com/v1.1/devices'

# Send the request (GET in this example)
response = requests.get(url, headers=apiHeader)

# Print the response
print(response.status_code)
print(response.json())
jmo5k02 commented 3 weeks ago

What python version are you using?

jmo5k02 commented 3 weeks ago

I see what you mean but I am entering them correctly. Mayber my account is not properly set up? However, I followed these steps from the documentation:

  1. Download the SwitchBot app on App Store or Google Play Store
  2. Register a SwitchBot account and log in into your account
  3. Generate an Open Token within the app a) Go to Profile > Preference b) Tap App Version 10 times. Developer Options will show up c) Tap Developer Options d) Tap Get Token
  4. Roll up your sleeves and get your hands dirty with SwitchBot OpenAPI!
hsakoh commented 3 weeks ago
>python --version
Python 3.11.3
hsakoh commented 3 weeks ago

@jmo5k02 For your reference, I'll provide the access token and secret for the account I just created using a temporary email address. (I'll reset the token in a few hours.) The BotRegion is AP.

token = '3bec07fef276a86af886d372dc00ae0db81ccbe633e74feee28f86fcb076d198ff4b2885bdb15adef343ac3c8eedb39c' secret = '5e764b3b5ffbf93f0eb4f59d3053a0ee'

With this token and secret, I have confirmed the following response using the same code.

Authorization: 3bec07fef276a86af886d372dc00ae0db81ccbe633e74feee28f86fcb076d198ff4b2885bdb15adef343ac3c8eedb39c
t: 1724315072855
sign: qkKinyDhDfz7xSlip/TxLOmv8U3R1edy6BRmeV5T1A4=
nonce: ccc06ab7-b153-4cad-8d5e-3ca05816e37b
200
{'statusCode': 100, 'body': {'deviceList': [], 'infraredRemoteList': []}, 'message': 'success'}
jmo5k02 commented 3 weeks ago

Ok Thank you. Then it must have something to do with the setup of my account.

ThatProgrammerr commented 2 weeks ago

From my experience, you have to regenerate your token if you intend to use the 1.1 API. I made the same mistake when I started with the 1.0 API.

milty456 commented 2 weeks ago

I have this same issue.

jmo5k02 commented 2 weeks ago

Hi Guys, the problem was definetly connected with my account setup. However, I can not say what the problem was. I created a different account and regenerated access token and secret key and everything worked fine with this code:

import json
import time
import hashlib
import hmac
import base64
import uuid
import requests

# Declare empty header dictionary
apiHeader = {}

token = 'tokenstring'
secret = 'secretstring'

nonce = uuid.uuid4()
t = int(round(time.time() * 1000))
string_to_sign = '{}{}{}'.format(token, t, nonce)

string_to_sign = bytes(string_to_sign, 'utf-8')
secret = bytes(secret, 'utf-8')

sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())
print ('Authorization: {}'.format(token))
print ('t: {}'.format(t))
print ('sign: {}'.format(str(sign, 'utf-8')))
print ('nonce: {}'.format(nonce))

#Build api header JSON
apiHeader['Authorization']=token
apiHeader['Content-Type']='application/json'
apiHeader['charset']='utf8'
apiHeader['t']=str(t)
apiHeader['sign']=str(sign, 'utf-8')
apiHeader['nonce']=str(nonce)

url = 'https://api.switch-bot.com/v1.1/'

# Send the request (GET in this example)
response = requests.get(url, headers=apiHeader)

# Print the response
print(response.status_code)
print(response.json())

Thanks for you help everyone.

milty456 commented 2 weeks ago

I got further too, but now im having issues sending a command

{'message': "Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header. Authorization=ksl;djfl;kjsldfj;alskjdf;lakjsdl;fkja;sldkjf;lasjkdslkdfjs"}

import json import time import hashlib import hmac import base64 import uuid import requests

import responses

apiHeader = {}

token = '1111111111111111111111111111111111111111111111111111111111111111111111111111111' # copy and paste from t>

secret = '22222222222222222222222222222' # copy and paste from the SwitchBot app V6.14 or later nonce = uuid.uuid4()

t = int(round(time.time() * 1000)) string_to_sign = '{}{}{}'.format(token, t, nonce)

string_to_sign = bytes(string_to_sign, 'utf-8') secret = bytes(secret, 'utf-8')

sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest()) print ('Authorization: {}'.format(token)) print ('t: {}'.format(t)) print ('sign: {}'.format(str(sign, 'utf-8'))) print ('nonce: {}'.format(nonce))

Build api header JSON

apiHeader['Authorization']=token apiHeader['Content-Type']='application/json' apiHeader['charset']='utf8' apiHeader['t']=str(t) apiHeader['sign']=str(sign, 'utf-8') apiHeader['nonce']=str(nonce)

url="https://api.switch-bot.com/v1.1/devices/D55555555/commands/press" response = requests.get(url, headers=apiHeader) print(response.status_code) print(response.json())