3

I've retrieved the username and password from a UITextField and now I want to set the username and password to every request Moya performs with basic auth.

How do I do this?

Berry
  • 2,143
  • 4
  • 23
  • 46

2 Answers2

3

The documentation that covers Basic Authentication is here

Here are the required parts you need

HTTP auth is a username/password challenge built into the HTTP protocol itself. If you need to use HTTP auth, you can provide a CredentialsPlugin when initializing your provider.

let provider = MoyaProvider<YourAPI>(plugins: [CredentialsPlugin { _ -> URLCredential? in
    return URLCredential(user: "user", password: "passwd", persistence: .none)
  }
])

This specific examples shows a use of HTTP that authenticates every request, which is usually not necessary. This might be a better idea:

let provider = MoyaProvider<YourAPI>(plugins: [CredentialsPlugin { target -> URLCredential? in
    switch target {
      case .targetThatNeedsAuthentication:
        return URLCredential(user: "user", password: "passwd", persistence: .none)
      default:
        return nil
    }
  }
])
Scriptable
  • 19,402
  • 5
  • 56
  • 72
  • Is there a way to set this to every `Provider` or do I really need to write this code for every `Provider` the app needs? – Berry Apr 28 '17 at 11:06
  • 1
    most apps only have one provider per API from my experience, in every app that I've worked on we just have one master provider. You should be able to set it dynamically... but I'm not familiar with that I'm afraid – Scriptable Apr 28 '17 at 11:09
0

This solution don't work for me.

The problem is that the URLCredential is only used for authentication challenges, not for preauthorizing the request. So if your API requires an Authorization header with Base64 "username:password" param this won't work. There seems to be a lot of confusion around how this works, see this similar issue on Alamofire.

I worked around this with the following:

SOLUTION 1

lazy var provider: MoyaProvider<LoginService> = {
    let endpointClosure = { (target: LoginService) -> Endpoint<LoginService> in
      let defaultEndpoint = MoyaProvider.defaultEndpointMapping(for: target)

      switch target {
      case .login(let username, let password):
        return defaultEndpoint.adding(newHTTPHeaderFields: ["Authorization": "Basic " + "\(username):\(password)".data(using: .nonLossyASCII)!.base64EncodedString(options: []))
      }
    }

    return MoyaProvider(endpointClosure: endpointClosure)
}()

Here is other solution with Moya Plugin approach:

SOLUTION 2

class AuthProvider {

    static let basicAuthPlugin: PluginType = AccessTokenPlugin(tokenClosure: { () -> String in
        guard let loginData = String(format: "\("username"):\("password")").data(using: .utf8) else { return "" }
        return loginData.base64EncodedString()
    })
}

let AuthAPIProvider = MoyaProvider<AuthAPI>(plugins: [AuthProvider.basicAuthPlugin])

enum AuthAPI {
    // ... methods
}

extension AuthAPI: AccessTokenAuthorizable {
    var authorizationType: AuthorizationType {
        return .basic
    }
}
Argus
  • 2,241
  • 1
  • 22
  • 27