How to verify token from SWIFT frontend in Flask Backend when setting up Apple log-in?

147 Views Asked by At

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.

0

There are 0 best solutions below