Failed to initialize Amplify with PluginError: Unable to decode configuration - ios

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.

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())))

Amplify ios signin with custom flow

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

Firebase AppCheck on iOS: 403 permission errors - PERMISSION_DENIED

Q:
How can permission errors be resolved for Firebase App Check?
Background:
I have enabled App Check per the documents:
DeviceCheck is enabled/configured per:
https://firebase.google.com/docs/app-check/ios/devicecheck-provider
App Attest is enabled configured per:
https://firebase.google.com/docs/app-check/ios/devicecheck-provider
SDK is added to the project, with code from:
https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/AppDelegate.swift
Specifically, in appdelegate:
Token setup:
FirebaseApp.configure()
requestDeviceCheckToken()
requestDebugToken()
if #available(iOS 14.0, *) {
requestAppAttestToken()
}
calling:
// MARK: App Check providers
func requestDeviceCheckToken() {
guard let firebaseApp = FirebaseApp.app() else {
return
}
DeviceCheckProvider(app: firebaseApp)?.getToken { token, error in
if let token = token {
print("DeviceCheck token: \(token.token), expiration date: \(token.expirationDate)")
}
if let error = error {
print("DeviceCheck error: \((error as NSError).userInfo)")
}
}
}
func requestDebugToken() {
guard let firebaseApp = FirebaseApp.app() else {
return
}
if let debugProvider = AppCheckDebugProvider(app: firebaseApp) {
print("Debug token: \(debugProvider.currentDebugToken())")
debugProvider.getToken { token, error in
if let token = token {
print("Debug FAC token: \(token.token), expiration date: \(token.expirationDate)")
}
if let error = error {
print("Debug error: \(error)")
}
}
}
}
#available(iOS 14.0, *)
func requestAppAttestToken() {
guard let firebaseApp = FirebaseApp.app() else {
return
}
guard let appAttestProvider = AppAttestProvider(app: firebaseApp) else {
print("Failed to instantiate AppAttestProvider")
return
}
appAttestProvider.getToken { token, error in
if let token = token {
print("App Attest FAC token: \(token.token), expiration date: \(token.expirationDate)")
}
if let error = error {
print("App Attest error: \(error)")
}
}
}
requestDeviceCheckToken()returns a permissions error:
DeviceCheck error: ["NSLocalizedFailureReason": The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDeviceCheckToken
- HTTP status code: 403
- Response body: {
"error": {
"code": 403,
"message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_IOS_APP_BLOCKED",
"domain": "googleapis.com",
"metadata": {
"service": "firebaseappcheck.googleapis.com",
"consumer": "projects/<my project #>"
}
}
]
}
}
requestDebugToken() returns a permissions error:
Debug error: Error Domain=com.firebase.appCheck Code=0 "The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDebugToken
- HTTP status code: 403
- Response body: {
"error": {
"code": 403,
"message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_IOS_APP_BLOCKED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/<my project #>",
"service": "firebaseappcheck.googleapis.com"
}
}
]
}
}
" UserInfo={NSLocalizedFailureReason=The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDebugToken
- HTTP status code: 403
- Response body: {
"error": {
"code": 403,
"message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_IOS_APP_BLOCKED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/<my project #",
"service": "firebaseappcheck.googleapis.com"
}
}
]
}
}
}
requestAppAttestToken() returns an error:
App Attest error: Error Domain=com.firebase.appCheck Code=0 "(null)"
GCP Console does show all calls to the following w/ 100% errors:
google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDebugToken
google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDeviceCheckToken
google.firebase.appcheck.v1beta.TokenExchangeService.GenerateAppAttestChallenge
All of which seem to point to a permissions error? Specifically, GOOGLE_APP_ID is in the request URL, but App Check is configured in Firebase via the console...
I'm not seeing anything in the docs or anything obvious in IAM that I missed? :(
Ty in advance for help!
Update
After further testing w/ Postman:
The issue seems to be that the SDK isn't passing the X-Ios-Bundle-Identifier correctly when calling AppCheck API(s).
Steps to get to this conclusion:
From POSTMAN: API call w/ original API_KEY -> yields initial (above) error response/403
From POSTMAN: API call as above, + X-Ios-Bundle-Identifier + valid debug_token -> yields success payload.
So:
any ideas to help ID why the X-Ios-Bundle-Identifier isn't being passed by the SDK? The app is using other Firebase API's w/out issue, so seems limited to the AppCheck SDK...
and/or - can the X-Ios-Bundle-Identifier be programmatically added (in Swift) to the AppCheck calls (it is properly notated in the .plist)
Resolved!
App Check SDK does not currently support the Android / iOS Application Restriction for API keys. With that, you must remove the App Restriction for your API keys to resolve this issue.
Hopefully, the Application Restriction(s) will be supported at some point...
Update!
v8.8.0-beta now supports the bundle ID! :)
1. Configure the private key for DeviceCheck
Make sure you have created a private key for DeviceCheck
And installed it in firebase project settings under AppCheck tab
https://firebase.google.com/docs/app-check/ios/devicecheck-provider
2. Add debug token to firebase.
If you use AppCheckDebugProvider (basically for simulators), after run the project you will see a debug token in the console, you need to copy it and add to AppCheck of the project settings. Than AppCheck will approve it. Also don't forget to add -FIRDebugEnabled for the Arguments Passed on Launch.
https://firebase.google.com/docs/app-check/ios/debug-provider
3. Add production entitlements for AppAttest environment.
The beta version of AppCheck doesn't work with the AppAttest development environment, so you need to setup the production environment in your entitlements. By default, AppAttest works in a development environment, and regardless of your choice in the market it will work with a production.
https://firebase.google.com/docs/app-check/ios/app-attest-provider
https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment
4. Optional:
You can simplify the code
#if targetEnvironment (simulator)
let providerFactory = AppCheckDebugProviderFactory ()
#else
let providerFactory = CustomAppCheckProviderFactory ()
#endif
AppCheck.setAppCheckProviderFactory (providerFactory)
And getting a token
if let fbApp = FirebaseApp.app () {
providerFactory.createProvider(with: fbApp)?.getToken { token, error in
if let token = token {
print ("AppCheck token: \ (token.token), expiration date: \ (token.expirationDate)")
} else if let error = error {
print ("AppCheck error: \ (error as NSError).userInfo)")
}
}
}
Or if you want to protect non-firebase resources, you can get a token like this:
AppCheck.appCheck().token (forcingRefresh: false) { token, error in
if let token = token {
print ("AppCheck token: \ (token.token), expiration date: \ (token.expirationDate)")
} else if let error = error {
print ("AppCheck error: \ (error as NSError).userInfo)")
}
}
https://firebase.google.com/docs/app-check/ios/custom-resource
App Check SDK does not currently support the Android / iOS Application Restriction for API keys. With that, you must remove the App Restriction for your API keys to resolve this issue.
Hopefully, the Application Restriction(s) will be supported at some point...
Got this error - here is what worked for me:
Run app on real Android device
Opened Android Studio → Logcat → search for “DebugAppCheckProvider” → copy the debug secret
In Firebase Go to “App Check” → Apps → 3 dot menu → Manage debug token → Add token → name it → paste the debug secret
Add console log of the token after activation of app check.
try {
await firebase.appCheck().activate("ignored", true);
const token = await getAppCheckToken();
console.log({ token });
} catch (err) {
console.error(err);
}
};

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

Firebase Storage iOS put file completion handler called before upload finishes

I was trying to upload videos to Firebase Storage, and when the upload finishes, I store the location of this video file to an object in the database, my code is as below:
Storage.storage().reference(withPath: mediaURL!).putFile(from: localCacheURL, metadata: nil, completion: { (metadata, error) in
if error != nil {
print("❗️failed to upload video")
} else {
print("a video is uploaded")
json = ["text": "", "image": "", "video": mediaURL!, "connections": []]
upload()
}
})
But I found the completion handler called before the upload actually finishes, I went to Firebase console and downloaded the uploaded file, it was unfinished.
Does anybody know why?
One for your reason for your problem may be that your cache file is somehow broken. To figure it out what's happening you need more information.
You can do that by observing the FIRFileStorageUploadTaskwhich is returned by putFile.
let uploadTask = Storage.storage().reference(withPath: mediaURL!).putFile(from: localCacheURL, metadata: nil, completion: { (metadata, error) in
if error != nil {
print("❗️failed to upload video")
} else {
print("a video is uploaded")
json = ["text": "", "image": "", "video": mediaURL!, "connections": []]
// unclear what this does
// looks strange if it's just a notification rename it into something like videoUploadCompleted
// if it needs the json stuff pass it as parameter to make it thread safe
upload()
}
})
uploadTask.observe(.progress) { snapshot in
print("\(snapshot.progress)")
}
It will give you something like:
<NSProgress: 0x604000123020> :
Parent: 0x0 / Fraction completed: 0.0062 / Completed: 1867836 of
301329576
You can also get more information about failures by using
uploadTask.observe(.failure) { snapshot in
if let error = snapshot.error as? NSError {
print ("Error : \(error)")
}
}
For more details about monitoring and error handling see also Upload Files on iOS Firebase Documentation

Resources