I am building an IOS application with a Python Flask backend. Specifically, I am now working on creating an Apple Login for my app. Yet, I have some issues with sending and receiving tokens to authenticate. When starting the IOS app, I get back the following backend logs:
Error decoding token. Token: eyJraWQiOiJmaDZCczhDIiwiYWxnIjoiU...[removed for brevity]...I9s-a89gzuTaZVL6sj1mZgtDw, Error: The specified alg value is not allowed
2023-09-27 09:02:28 backend[20230927t102817] Received token: %s eyJraWQiOiJmaDZCczhDIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHR...[removed for brevity]...w9s-a89gzuTaZVL6sj1mZgtDw
2023-09-27 09:02:29 backend[20230927t102817] eyJhbGciOiJFUzI1NiIsvYXBwbGVpZC5hcHB...[removed for brevity]...sZS5jb20iLCJleHAiOjE2OTU4M
When clicking on Sign-In, I get the following logs:
Authorization token is missing in the request header.
2023-09-27 09:33:45 backend[20230927t112025] Received token: %s eyJraWQiOiJmaDZCczhDIiwiYWxnIjoiUlMyNTYifQ.eyJpc3Ms...[removed for brevity]...cxMH02zQq3LRW5rWXe8JnFg
2023-09-27 09:33:46 backend[20230927t112025] eyJhbGciOiJFUzI1NiIsImF...[removed for brevity]...1ZCI6ImhiWHM3N4H_RaFtE7o9w
My backend looks the following:
ACCESS_TOKEN_EXPIRE_MINUTES = 60 # 1 hour
REFRESH_TOKEN_EXPIRE_DAYS = 7 # 7 days
JWT_SECRET = os.environ["JWT_SECRET"]
JWT_ALGORITHM = "HS256"
# For demonstration purposes, tokens are stored in memory
# In a real-world scenario, you'd want to use a database like Redis
BLACKLIST = set()
def create_token(data: dict, expires_delta: timedelta):
try:
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
print(
f"Token created successfully. Data: {data}, Expires Delta: {expires_delta}"
)
return encoded_jwt
except Exception as e:
print(
f"Error creating token. Data: {data}, Expires Delta: {expires_delta}, Error: {e}"
)
raise e
@app.before_request
def before_request_func():
try:
if request.endpoint in ["health_check", "readiness_check"]:
return
token = request.headers.get("Authorization")
if token:
if token in BLACKLIST:
print(f"Token is in the blacklist. Token: {token}")
g.user = None
else:
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
g.user = payload.get("user_id")
print(f"Token decoded successfully. User ID: {g.user}")
except jwt.PyJWTError as e:
print(f"Error decoding token. Token: {token}, Error: {e}")
g.user = None
else:
print("Authorization token is missing in the request header.")
g.user = None
except Exception as e:
print(f"Error in before_request_func. Error: {e}")
raise e
@app.route("/auth/apple", methods=["POST"])
def apple_auth():
try:
token = request.json.get("token") # This is the token from the frontend
except Exception as e:
print(f"Error getting token from request: {e}")
return jsonify(error="Internal server error"), 500
if not token:
print("Missing Apple token in request")
return jsonify(error="Missing Apple token"), 400
print("Received token: %s", token) # Mask or truncate token value for security
try:
headers = {
"kid": APPLE_KEY_ID,
"alg": "ES256",
"iss": APPLE_TEAM_ID,
"aud": "https://appleid.apple.com",
"exp": int(time.time()) + 3600,
"iat": int(time.time()),
}
except Exception as e:
print(f"Error handling headers {e}")
return jsonify(error="Internal server error"), 500
# Generate client secret
try:
client_secret = jwt.encode(
{
"iss": APPLE_TEAM_ID,
"iat": int(time.time()),
"exp": int(time.time()) + 3600,
"aud": "https://appleid.apple.com",
"sub": APPLE_CLIENT_ID,
},
APPLE_PRIVATE_KEY,
algorithm="ES256",
headers=headers,
)
print(client_secret)
except Exception as e:
print(f"Error generating client secret: {e}")
return jsonify(error="Internal server error"), 500
# Validate token with Apple
try:
response = requests.post(
"https://appleid.apple.com/auth/token",
headers={
"content-type": "application/x-www-form-urlencoded",
"accept": "application/json",
},
data={
"client_id": APPLE_CLIENT_ID,
"client_secret": client_secret,
"code": token,
"grant_type": "authorization_code",
# 'redirect_uri': 'YOUR_REDIRECT_URI' # @akop only if you provided a URI, we need to fill this in (not necessary for mobile apps) -> If you provided one when setting up your app on Apple Developer Portal
},
)
response.raise_for_status() # This will raise an HTTPError if the HTTP request returned an unsuccessful status code
print(response)
except requests.HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
print(f"Response Details: {response.text}")
return jsonify(error="Internal server error"), 500
except Exception as e:
print(f"Error validating with Apple: {e}")
return jsonify(error="Internal server error"), 500
print("Validated token with Apple successfully")
try:
data = response.json()
except Exception as e:
print(f"Error parsing response JSON: {e}")
print(f"Response content: {response.text}")
return jsonify(error="Internal server error"), 500
if "error" in data:
print(f"Error from Apple authentication: {data['error']}")
return jsonify(error=data["error"]), 400
# # this is for a jwt token (so that the user doesn't need to login all the time)
user_data = {"user_id": data.get("sub")}
access_token = create_token(
data=user_data, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
print(user_data)
refresh_token = create_token(
data=user_data, expires_delta=timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
)
print(refresh_token)
# Rest of your code remains unchanged
print("Returning access and refresh tokens successfully")
return jsonify({"access_token": access_token, "refresh_token": refresh_token})
And the frontend (the SWIFT client) where I verify the server with token, looks like this:
func verifyTokenWithServer(token: String, completion: @escaping (Bool) -> Void) {
let url = URL(string: "https://backend-myurl.appspot.com/auth/apple")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let parameters = ["token": token]
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
How to fix these errors? Thanks in advance.
I tried the following: changing decoding algo, changing header/body token sending.