Swift
HTTP Request
Basic Authentication
Swift Programming
Networking

How to make an HTTP request basic auth in Swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Basic Authentication in Swift is straightforward to implement, but it must be done carefully to avoid insecure credential handling. The core idea is to send an Authorization header with a Base64-encoded username:password string over HTTPS. This guide shows practical request patterns, error handling, and security improvements for production apps.

Core Topic Sections

Understand Basic Auth in practice

Basic Auth sends credentials with each request. Base64 encoding is not encryption, so transport security is mandatory.

Required rule:

  1. Use HTTPS only.
  2. Never send Basic Auth over plain HTTP.

If the server supports token-based flows, Basic Auth is often used only once to exchange for a short-lived token.

Build the authorization header in Swift

swift
1import Foundation
2
3func basicAuthHeader(username: String, password: String) -> String {
4    let raw = "\(username):\(password)"
5    let data = Data(raw.utf8)
6    let base64 = data.base64EncodedString()
7    return "Basic \(base64)"
8}
9
10print(basicAuthHeader(username: "api-user", password: "secret"))

This helper keeps header generation isolated and testable.

Make a GET request with URLSession

swift
1import Foundation
2
3func fetchProfile() async throws -> Data {
4    guard let url = URL(string: "https://api.example.com/profile") else {
5        throw URLError(.badURL)
6    }
7
8    var request = URLRequest(url: url)
9    request.httpMethod = "GET"
10    request.setValue(basicAuthHeader(username: "api-user", password: "secret"), forHTTPHeaderField: "Authorization")
11    request.setValue("application/json", forHTTPHeaderField: "Accept")
12
13    let (data, response) = try await URLSession.shared.data(for: request)
14
15    guard let http = response as? HTTPURLResponse else {
16        throw URLError(.badServerResponse)
17    }
18    guard (200...299).contains(http.statusCode) else {
19        throw NSError(domain: "HTTP", code: http.statusCode)
20    }
21
22    return data
23}

This pattern is compact and production-friendly for many API clients.

Make a POST request with JSON body

swift
1import Foundation
2
3struct LoginPayload: Encodable {
4    let deviceId: String
5}
6
7func postSession(deviceId: String) async throws -> Data {
8    guard let url = URL(string: "https://api.example.com/session") else {
9        throw URLError(.badURL)
10    }
11
12    var request = URLRequest(url: url)
13    request.httpMethod = "POST"
14    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
15    request.setValue(basicAuthHeader(username: "api-user", password: "secret"), forHTTPHeaderField: "Authorization")
16    request.httpBody = try JSONEncoder().encode(LoginPayload(deviceId: deviceId))
17
18    let (data, response) = try await URLSession.shared.data(for: request)
19    guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else {
20        throw URLError(.cannotParseResponse)
21    }
22    return data
23}

For modern Swift code, async and await gives cleaner flow than callback nesting.

Avoid hardcoded credentials

Never keep production credentials in source code. Safer options:

  1. Store user secrets in Keychain.
  2. Use secure backend-provided tokens where possible.
  3. Inject credentials via secure config for debug builds only.

Even in internal apps, source-level secrets are frequently leaked through logs or screenshots.

Handle auth failures and retries correctly

On 401 responses:

  1. Do not retry indefinitely with same credentials.
  2. Clear invalid cached credential state.
  3. Prompt re-authentication or refresh token flow.

Also log status code and request identifier, not raw credential strings.

Use URLCredential when challenge flow is required

Some servers challenge first and expect credential response. Delegate-based handling can be useful.

swift
1import Foundation
2
3final class AuthDelegate: NSObject, URLSessionDelegate {
4    func urlSession(_ session: URLSession,
5                    didReceive challenge: URLAuthenticationChallenge,
6                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
7        let credential = URLCredential(user: "api-user", password: "secret", persistence: .forSession)
8        completionHandler(.useCredential, credential)
9    }
10}

Header-based auth is still more common for API-style Basic Auth, but challenge handling matters for some legacy endpoints.

Testing strategy

Validate these scenarios:

  1. Success with valid credentials.
  2. Unauthorized response with invalid credentials.
  3. Network timeout handling.
  4. JSON decode failure handling.

A small mocked API test harness prevents regressions in networking layers.

Common Pitfalls

  • Sending Basic Auth over non-HTTPS endpoints.
  • Treating Base64 as encryption and exposing credentials carelessly.
  • Hardcoding credentials in source code and committing secrets.
  • Retrying failed auth requests without handling 401 correctly.
  • Logging full request headers that include Authorization values.

Summary

  • Basic Auth in Swift is implemented through the Authorization header.
  • Use URLSession with async and await for clean request code.
  • Enforce HTTPS and secure credential storage from the start.
  • Add explicit handling for 401, timeouts, and parse failures.
  • Prefer token exchange flows when backend supports stronger auth models.

Course illustration
Course illustration

All Rights Reserved.