Amplify ios signin with custom flow - ios

I am trying to implement a custom signin flow using amplify ios library and cognito.
The flow is based on this passwordless implementation https://github.com/mobilequickie/amplify-passwordless-sms-auth/tree/68152489152e1fc4c3185f4e5e3383639bdc8285, it works great on web, but I can't make it work on ios, I get the following error:
-------Sign In response---------
failure(AuthError: Incorrect username or password.
Recovery suggestion: Check whether the given values are correct and the user is authorized to perform the operation.)
Please find below the relevant code:
public init(_ secureService: SecureServiceProtocol) {
self.secureService = secureService
self.token = secureService.get(tokenKey)
self.authModel = secureService.get(authKey, type: AuthModel.self)
do {
let url = Bundle.main.url(forResource: "amplifyconfiguration", withExtension: "json")!
let configuration = try AmplifyConfiguration(configurationFile: url)
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.configure(configuration)
if authModel != nil {
self.retrieveAuthData { _ in }
}
} catch {
L.log(type: .error, message: error.localizedDescription)
print(error)
print(error.asAFError)
}
}
public func accessWith(_ phone: String, callback: #escaping AuthResultCallback) {
print(phone)
Amplify.Auth.signIn(username: phone) { result in
print("-------Sign In response---------")
print(result)
}
}
configuration
{
"auth": {
"plugins": {
"awsCognitoAuthPlugin": {
"IdentityManager": {
"Default": {}
},
"CredentialsProvider": {
"CognitoIdentity": {
"Default": {}
}
},
"CognitoUserPool": {
"Default": {
"Region": "eu-west-2",
"PoolId": "eu-west-2xxxxxx",
"AppClientId": "5vmjioxxxxxxxxxx"
}
}
},
"Auth": {
"Default": {
"authenticationFlowType": "CUSTOM_AUTH"
}
}
}
}
}

I have been facing the same issue and found this
The root cause for our issue was that the iOS Amplify library always sends an initial ChallengeName of SRP_A to the Cognito signIn call. However, the example "Define Auth Challenge trigger" is explicitly coded to fail any authentication calls where the ChallengeName is not CUSTOM_CHALLENGE.
So you need to port that same behavior with these lambdas. Because the Define lambda looks for the CUSTOM_CHALLENGE ChallengeName and fails requests that have a different ChallngeName, the logic is incompatible with the iOS Amplify libraries as-is, since they initially send SRP_A.
I was able to work around this by modifying the Define Auth Challenge lambda to respond with the CUSTOM_CHALLENGE name instead of failing outright, and that seems to have fixed up the iOS side.
You can use the lambda's from here

Related

iOS: Amplify always storing files to public directory

I am using Amplify library to store files from iOS to AWS storage. My code looks something like this:
class UploadServiceController {
static let `default` = UploadServiceController()
init() {
Amplify.Logging.logLevel = .verbose
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.add(plugin: AWSS3StoragePlugin())
try Amplify.configure()
} catch {
assert(false, "An error occurred setting up Amplify: \(error)")
}
}
func upload(data: Data, for filePath: String) -> UploadServiceOperation {
let storageOperation = Amplify.Storage.uploadData(key: "media/images", data: data)
return UploadServiceOperation(storageOperation: storageOperation)
}
}
storage json:
"storage": {
"plugins": {
"awsS3StoragePlugin": {
"bucket": "native-media-storage",
"region": "eu-central-1"
}
}
}
However when I perform upload my images are stored to: native-media-storage/public/media/images, instead of native-media-storage/media/images. I have browsed SO, I found solution for javascript: AWS amplify adding files in public directory, but nothing for iOS.
How can this be done on iOS?
While Amplify Docs leave a lot to be desired, browsing through their github, I found PR that adds this functionality. The PR is from September 2021, and here is the solution:
// MARK: - Custom Prefix Resolver
private struct CustomPrefixResolver: AWSS3PluginPrefixResolver {
func resolvePrefix(for accessLevel: StorageAccessLevel,
targetIdentityId: String?) -> Result<String, StorageError> {
return .success("")
}
}
and use it like this:
try Amplify.add(plugin: AWSS3StoragePlugin(configuration: .prefixResolver(CustomPrefixResolver())))

Failed to initialize Amplify with PluginError: Unable to decode configuration

Whenever im trying to upload an image, app crashes, after investigating the issue, I reached the following:
when calling Amplify.configure, its failing and im getting the following error:
Failed to initialize Amplify with PluginError: Unable to decode configuration
Recovery suggestion: Make sure the plugin configuration is JSONValue.
the upload code where the app is crashing is as follow:
Amplify.Storage.uploadData(key: String(actualKey), data: data) { (event) in
..... }
my code is as following in app delegate:
private func setupAWS() {
do {
let storafePlugin = AWSS3StoragePlugin()
try Amplify.add(plugin: storafePlugin)
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.configure()
print("Amplify configured with storage plugin")
} catch {
print("Failed to initialize Amplify with \(error)")
}
}
the amplify json file is as follow:
{
"UserAgent": "aws-amplify-cli/2.0",
"Version": "1.0",
"storage": {
"plugins": {
"awsS3StoragePlugin": {
"bucket": "xxxxxx",
"region": "eu-central-1",
"defaultAccessLevel": "guest"
}
}
}
}
anyone knows what's going around?
thanks
UPDATE: when I remove the following:
try Amplify.add(plugin: AWSCognitoAuthPlugin())
the error disappears, but on image upload im getting a new error:
Fatal error: No plugins added to Authentication category.

Swift, Firebase Cloud Function - INVALID ARGUMENT error

I am trying to call a cloud function from firebase by using the following code.
Client code -
func checkUserStatus() {
let functions = Functions.functions(region: "us-central1")
let argument = [
"currentUser":
[
"email": "test#email.com",
"uid": "LP8R4yZroyMTj"
]
]
functions.httpsCallable("subscriptionStatus").call(argument) { (result, error) in
if error != nil {
print("FAILED")
print(error)
} else {
print("PASSED")
print(result)
}
}
}
Cloud Function code -
exports.subscriptionStatus = functions.https.onRequest(async (request: Request<RequestBody>, response) => {
const {
currentUser,
} = request.body
// Logic goes here
}
But getting the following error when running it
Error Domain=com.firebase.functions Code=3 "INVALID ARGUMENT" UserInfo={NSLocalizedDescription=INVALID ARGUMENT}
The function takes in a parameter called currentUser which further comprises of user's email and uid.
Any lead would be highly appreciated on the matter.
Just to have an answer to the question for anyone else having a similar issue.
To call an onRequest() cloud function you need to use the URL where it's deployed at i.e. https://us-central1-<project-id>.cloudfunctions.net/<function-name>?<var-name>=<var-value>
If you're wanting to call it in the client app using call, then you'll need to use an onCall() cloud function.
Firebase has a one of the best documentation on their services: https://firebase.google.com/docs/functions/get-started
OnCall functions:
https://firebase.google.com/docs/functions/callable
onRequest functions:
https://firebase.google.com/docs/functions/http-events

Getting an AuthToken using Siesta via Password Grant

new to both Swift and Siesta... Trying to make a "password" grant type request. I used the code located here (the block at the very bottom). My code is:
var authToken: String??
var tokenCreationResource: Resource { return resource("oauth/v2/token") }
func refreshTokenOnAuthFailure(request: Request) -> Request {
return request.chained {
guard case .failure(let error) = $0.response, // Did request fail…
error.httpStatusCode == 401 else { // …because of expired token?
return .useThisResponse // If not, use the response we got.
}
return .passTo(
self.createAuthToken().chained { // If so, first request a new token, then:
if case .failure = $0.response { // If token request failed…
return .useThisResponse // …report that error.
} else {
//print($0.response)
return .passTo(request.repeated()) // We have a new token! Repeat the original request.
}
}
)
}
}
func userAuthData() -> [String: String] {
return [
"username": "username",
"password": "password",
"grant_type": "password",
"client_id": "abc1234567",
"client_secret": "1234567abc"
]
}
func createAuthToken() -> Request {
print("requestingToken")
return tokenCreationResource
.request(.post, urlEncoded: userAuthData())
.onSuccess {
self.authToken = $0.jsonDict["access_token"] as? String // Store the new token, then…
print($0.jsonDict) //*****SEE MY NOTE BELOW ABOUT THIS LINE
self.invalidateConfiguration() // …make future requests use it
}
}
The problem is that it doesn't seem to set the authToken variable... In troubleshooting the $0.jsonDict variable in the createAuthToken() function seems to be empty. The line noted prints [:]
If I change the print($0.jsonDict) to print($0) I see the full response including the "content" section which displays the results I would have expected to be in the jsonDict
If it matters, my server implementation is Symfony using FOSOauthServerBundle. All this works fine if I just manually do a request in the browser and like I said the "content" of the response shows my token, I just can't seem to access it via the .jsonDict["access_token"]
Had this exact same issue with the example code - you need to remove
standardTransformers: [.text, .image]
from the Service constructor (or include .json).

What are the possible causes of error 14000, "Fail to start messaging", in the SendBird iOS SDK?

I'm trying to use the SendBird SDK to implement a simple instant messaging feature in my iOS app.
I've read the Quick Start, Authentication, and Messaging Channel - Basic guides, and I've attempted to set up a 1-1 messaging channel as they describe.
In application:didFinishLaunchingWithOptions I included SendBird.initAppId("MyAppID").
In my initial view controller, I log in the user. I'm using the user's username from my app as his userId, and I'm concatenating his first and last name to use as his SendBird nickname:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if let emailAddress = KCSUser.activeUser().email,
firstName = KCSUser.activeUser().givenName,
lastName = KCSUser.activeUser().surname {
let nickname = firstName.lowercaseString + lastName.lowercaseString
SendBird.loginWithUserId(emailAddress, andUserName: nickname)
}
}
Finally, in the actual chat view controller, I attempt to start a messaging channel with another user:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
guard let targetId = session?.parent?.user?.email else {
return
}
// I logged in once with this id, so according to the SendBird docs, the
// user should exist.
SendBird.startMessagingWithUserId(targetId)
SendBird.setEventHandlerConnectBlock({ (channel) in
NSLog("%#", channel)
}, errorBlock: { (errorCode) in
NSLog("%D", errorCode)
}, channelLeftBlock: { (channel) in
}, messageReceivedBlock: { (message) in
}, systemMessageReceivedBlock: { (systemMessage) in
}, broadcastMessageReceivedBlock: { (broadcastMessage) in
}, fileReceivedBlock: { (fileLink) in
}, messagingStartedBlock: { (messagingChannel) in
SendBird.joinChannel(messagingChannel.getUrl())
// SendBirdChannelInfo is a custom struct for some use within the app
let channelInfo = SendBirdChannelInfo(
channelId: messagingChannel.getId(),
url: messagingChannel.getUrl()
)
// self.session is a custom data model for use with our database.
self.session?.sendBirdChannelInfo = channelInfo
SendBird.queryMessageListInChannel(messagingChannel.getUrl()).prevWithMessageTs(
Int64.max, andLimit: 30, resultBlock: { (queryResult) in
var maxMessageTs = Int64.min
for model in queryResult {
if maxMessageTs <= (model as! SendBirdMessageModel).getMessageTimestamp() {
maxMessageTs = (model as! SendBirdMessageModel).getMessageTimestamp()
}
}
SendBird.connectWithMessageTs(maxMessageTs)
}, endBlock: { (error) in
if let fetchMessagesError = error {
NSLog(fetchMessagesError.localizedDescription)
}
})
}, messagingUpdatedBlock: { (messagingChannel) in
}, messagingEndedBlock: { (messagingChannel) in
}, allMessagingEndedBlock: {
}, messagingHiddenBlock: { (messagingChannel) in
}, allMessagingHiddenBlock: {
}, readReceivedBlock: { (readStatus) in
}, typeStartReceivedBlock: { (typeStatus) in
}, typeEndReceivedBlock: { (typeStatus) in
}, allDataReceivedBlock: { (unsignedInt, thirtyTwoBitInt) in
}, messageDeliveryBlock: { (sent, message, data, messageId) in
}, mutedMessagesReceivedBlock: { (message) in
}) { (fileLink) in
}
}
With the exception of the lines I commented, this code comes directly from the SendBird manual. However, when it runs, I receive error code 14000, and the message, "Fail to start messaging," is logged.
What is the actual cause of the error? Am I missing a step during user login or SDK initialization, or is there another step in creating a channel? Or is it something else entirely?
Please try our new SDK instead of the old SDK which will be deprecated soon!
https://docs.sendbird.com/ios
Thanks!

Resources