0

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'})
  • @workingdogsupportUkraine thanks, how could I do that? – Daniel Curtis Jan 21 '23 at 02:22
  • Note, Apple requires `https` connection. To use `http`, you need to set the "NSAppTransportSecurity" in your `Info.plist` to allow `http` connection to the server. Have you tried `https://127.0.0.1:8000/posts` – workingdog support Ukraine Jan 21 '23 at 04:00
  • @workingdogsupportUkraine yes I have, but that wasn’t working either. I’ll have to try setting the “NSAppTransportSecurity” now – Daniel Curtis Jan 21 '23 at 12:20
  • this post: https://stackoverflow.com/questions/73847451/how-to-allow-loading-png-from-http-via-adding-nsexceptiondomains-in-ats/73848497#73848497 may help, replace `www.kittenswhiskers.com` with `localhost` – workingdog support Ukraine Jan 21 '23 at 12:39
  • @workingdogsupportUkraine it still doesn't seem to be working even when I change my app transport security settings. I also deployed it to heroku, which uses https, but the api still never receives the authorization header. I'm not too sure what else it could be. – Daniel Curtis Jan 22 '23 at 19:56
  • if you have a "demo" token that can be used, show the api/server address you want to reach, and I'll have a try. It is difficult to debug without some code that can reproduce the issue. – workingdog support Ukraine Jan 22 '23 at 22:53
  • If you insert there `headers).responseDecodable` : `headers).cURLDescription(calling: { print("curl equivalent:\n\($0)")}).responseDecodable`, does the header seems to be set? – Larme Jan 23 '23 at 09:24
  • @Larme I did that and the Authorization header is there – Daniel Curtis Jan 23 '23 at 15:21
  • @workingdogsupportUkraine here is a token you can use `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE2NzUwOTI2Nzl9.V-nvE7oybzXIetq8dr2qlXrxBtrnBeJQ36D0yEZUDu0` – Daniel Curtis Jan 23 '23 at 15:32
  • You'll want to use a web proxy like Proxyman or network inspector like Wireshark to see whether your header is actually transmitted. Technically, `URLSession` says it can take over the `Authorization` header, but that usually doesn't happen. I wonder if you're sending the header but the server isn't authenticating it and so prompts for another credential. – Jon Shier Jan 23 '23 at 21:02
  • good start with the demo token, I will try it, but I also need the address of the server, presumably on heroku. – workingdog support Ukraine Jan 23 '23 at 22:20
  • @workingdogsupportUkraine yes, of course, hahaha how could I forget! Here you go: https://fastapi-daniel855.herokuapp.com/ – Daniel Curtis Jan 24 '23 at 16:19

1 Answers1

0

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
            }
        }
    }
}