Search code examples
swiftauthenticationjwtalamofirefastapi

Why is my Authorization header being removed during my Request?


I am trying to send a request to an API from Xcode. I have JWT authentication set up, but when I am adding the Authorization header with my bearer token, it is not received on the server. I thought I wasn't doing it right, but my other headers are being sent correctly. I also can make authorization work from postman so i don't think it is my API. I am using the fastAPI framework for my API and Alamofire for making my requests in Swift. Does anyone else have a similar issue or know how to fix it?

This is what my code looks like when I make the request:

func getPosts() {
                 
        let headers: HTTPHeaders = [
            "Authorization":"Bearer \(UserDefaults.standard.object(forKey: "access_token") as! String)",
            "Content-type":"application/json"
        ]
        
        AF.request("\(mainURL)posts", method: .get, headers: headers).responseDecodable(of: Dictionary<String, String>.self) { response in
            debugPrint(response)
        }
        
}

I know my token is being sent correctly because when the debugPrint(response) is run it spits out this which includes the Authorization header:

[Request]: GET http://127.0.0.1:8000/posts
    [Headers]:
        Authorization: Bearer xxxx
        Content-Type: application/json
    [Body]: None
[Response]:
    [Status Code]: 401
    [Headers]:
        Content-Length: 30
        Content-Type: application/json
        Date: Fri, 20 Jan 2023 23:27:48 GMT
        Server: uvicorn
        Www-Authenticate: Bearer
    [Body]:
        {"detail":"Not authenticated"}
[Network Duration]: 0.13030695915222168s
[Serialization Duration]: 0.0005600140430033207s
[Result]: success(["detail": "Not authenticated"])

However when had my server print out the request headers it gave me this, which is missing the Authorization header, but the Content-type header set like it was supposed to:

Headers({'host': '127.0.0.1:8000', 'content-type': 'application/json', 'accept': '*/*', 'user-agent': 'APITest/1.0 (my-Name.APITest; build:1; iOS 16.2.0) Alamofire/5.6.4', 'accept-language': 'en;q=1.0', 'accept-encoding': 'br;q=1.0, gzip;q=0.9, deflate;q=0.8', 'connection': 'keep-alive'})

Solution

  • Ok, I tried your "demo" token and server endpoint, and all is working well for me.

    Here is the test code I used. It shows how to fetch, decode and display the data from the server, using your Alamofire code. The important thing here, is that your data models need to match the json data you receive from the server.

    Note especially, the last / in AF.request("\(mainURL)posts/", ..., that was probably the cause of your error.

    import Foundation
    import SwiftUI
    import Alamofire
    
    
    // MARK: - ApiResponse
    struct ApiResponse: Identifiable, Codable {
        let id = UUID()
        let post: Post
        let votes: Int
        
        enum CodingKeys: String, CodingKey {
            case post = "Post"
            case votes
        }
    }
    
    // MARK: - Post
    struct Post: Identifiable, Codable {
        let title, content: String
        let published: Bool
        let id, createdAt: String
        let ownerID: Int
        let owner: Owner
        
        enum CodingKeys: String, CodingKey {
            case title, content, published, id
            case createdAt = "created_at"
            case ownerID = "owner_id"
            case owner
        }
    }
    
    // MARK: - Owner
    struct Owner: Identifiable, Codable {
        let id, email, createdAt: String
        
        enum CodingKeys: String, CodingKey {
            case id, email
            case createdAt = "created_at"
        }
    }
    
    struct ContentView: View {
        @State var model: [ApiResponse] = []
        
        var body: some View {
            List(model) { response in
                Text(response.post.title)
            }
            .onAppear {
                getPosts() { results in
                    model = results
                }
            }
        }
        
        func getPosts(completion: @escaping  ([ApiResponse]) -> Void) {
            let mainURL = "https://fastapi-daniel855.herokuapp.com/"
            let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE2NzUwOTI2Nzl9.V-nvE7oybzXIetq8dr2qlXrxBtrnBeJQ36D0yEZUDu0"
            
            let headers: HTTPHeaders = [
                "Authorization": "Bearer \(token)",
                "Content-type": "application/json"
            ]
            
            AF.request("\(mainURL)posts/", method: .get, headers: headers).responseDecodable(of: [ApiResponse].self) { response in
                debugPrint(response)
                switch response.result {
                    case .success(let results): completion(results)
                    case .failure(_): completion([])  // <-- errors, todo
                }
            }
        }
    }