Problem accessing Azure SQL database table from iOS swift app - ios

I created a demo app that authenticates user from Azure Active Directory and able to perform sync with easy tables. When I connected App Service at Azure Portal with SQL database then fetching a table data in iOS swift app throws 401 error. Below is the code snippet.
let client = MSClient(applicationURLString: "URL String")
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext!
self.store = MSCoreDataStore(managedObjectContext: managedObjectContext)
client.syncContext = MSSyncContext(delegate: nil, dataSource: self.store, callback: nil)
self.table = client.syncTable(withName: "table name")
self.refreshControl?.addTarget(self, action: #selector(ActivityTableViewController.onRefresh(_:)), for: UIControlEvents.valueChanged)
API call (onRefresh call):
self.table!.pull(with: self.table?.query(), queryId: "AllRecords") {
(error) -> Void in DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
// throws 401 error here
}
}
Now I have bypassed the user authentication and in Azure App Service, easy table is working fine and I can access data from mobile app.
I connected SQL database with App Service and created a easy table with same name as I have in SQL database. It created the same schema in easy table as I have in SQL database. Now the problem is that I am not able to fetch data in mobile app from this table. SQL database has entries in table.

Related

How to validate fields server side during user creation with Firebase Auth?

I'm trying to use Firebase Auth to create a new user, but I want to validate some fields (pattern matching) using Firestore Security Rules before creating a new account. How can I do that?
In the completion handler for the createUser(withEmail: , password:) function, I am performing some writes to Firestore on successful account creation.
I am facing a problem where sometimes the writes to Firestore may not be successful due to Firestore Secuity Rules (Pattern matching). In this case the write fails but the new user account is still created (since writes are being attempted in completion handler).
// Create User Method - Firebase Auth & Swift
Auth.auth().createUser(withEmail: self.emailTextField.text!, password: self.passwordTextField.text!) { (result, error) in
if error != nil {
print(error?.localizedDescription)
} else {
let userName = [
userName:self.userNameTextField.text!
]
// Writing field Data to Firestore
Firestore.firestore().collection("users").document(self.userNameTextField.text!).setData(userName) {(err) in
if err != nil {
// Rather than throwing a fatalError, how can I ensure new account creation is cancelled so that feedback can be given on the issue with entered field data?
fatalError()
}
I want to ensure a user account is not created in case writes to Firestore are unsuccessful due to a conflict with Firestore Security Rules.
Firebase Authentication doesn't have any security rules. There's currently no way to check if incoming account properties are valid before a user gets created. Security rules only apply to data read and written directly to Cloud Firestore (or Realtime Database, or Cloud Storage) from a mobile or web client.
The only thing you could do is use a Cloud Functions auth trigger to check the account properties after it was created, then delete or deactivate the account if something is wrong.

Insufficient authentication scopes error for Google Sheet API for iOS

I'm trying to fetch a Google Sheet with the Google Sheets API for iOS. I've successfully added the user authentication since it's no longer asking for an API key, but I'm now getting a 403 error for:
Request had insufficient authentication scopes*.
I've added the following scopes in the Google Developers Console.
/auth/drive
/auth/spreadsheets
And here's the code I'm using to make and execute the query.
let potentialShowSheetID = "1n4wvD2vSSiAnG_pnD9rWR6dNCSnZz0pAGAiSYRaJCKs"
let service = GTLRSheetsService()
private let scopes = [kGTLRAuthScopeSheetsSpreadsheets] // This was specified in
// another Stack Overflow question, but I'm not sure how it would be used
// and it doesn't seem to help.
override func viewDidLoad() {
super.viewDidLoad()
service.authorizer = GIDSignIn.sharedInstance().currentUser.authentication.fetcherAuthorizer()
let query = GTLRSheetsQuery_SpreadsheetsGet.query(withSpreadsheetId: potentialShowSheetID)
service.executeQuery(query) { (ticket, data, error) in
print("GTLRService ticket: \(ticket)")
if error != nil {
print("GTLRService potential show query error: \(error!)")
}
else {
print("data: \(data ?? "unknown data")")
}
}
}
Is there a way to specify the scope in the query with the Google Sheets API for iOS? I looked through the framework pretty well and it doesn't ever seem to take that as a parameter. Maybe I'm missing something else.
UPDATE: I checked the granted scopes for the user and it doesn't include the Drive or Sheets API.
print("Scopes:", GIDSignIn.sharedInstance()?.currentUser.grantedScopes ?? "unknown scopes")
// Prints -> Scopes: [https://www.googleapis.com/auth/userinfo.profile, https://www.googleapis.com/auth/userinfo.email]
How can I specify the scopes to grant access to during authentication?
After some digging around the Google Sign In API headers, I found I just had to specify the scopes with this:
GIDSignIn.sharedInstance()?.scopes = [kGTLRAuthScopeSheetsSpreadsheets, kGTLRAuthScopeCalendar]
...before signing in with GIDSignInButton.

Manage users in firebase for multi tenant app

We have a ordering system which end users can order meals from their iOS app. Each iOS app belongs to a brand, each user also belongs to a brand. We put all brand information in one firebase project. The database structure is:
-brands
-- brand_id_1:
-- information
-- brand_id_2:
-- information
-stores
-- store_id_1:
-- brand_id:brand_id_1
-- more information
-- store_id_2:
-- brand_id:brand_id_1
-- more information
-orders
--brand_id_1:
--order_id_1:
--orderinfo
--brand_id_2:
--order_id_4:
--orderinfo
-users
-- user_id_1:
-- brand_id:brand_id_1
-- userinfo
-- user_id_2:
-- brand_id:brand_id_2
-- userinfo
We use Facebook and twitter authentication for sign in each app. However, one firebase project can only assign one Facebook app id. That means if user downloads brand1 app and sign in by Facebook , when he or she downloads brand2 app, the user account will be already created and our users will be confused. We hope each brand has their own user database, but we can still manage all the brands and stores data in one firebase project.
What we want to do is put all brands and stores in a main firebase project, then for each brand just create a firebase project for each iOS app. These firebase projects are just for user login (when sign up success put the uid to main firebase project), and all user orders will be saved to our main firebase project.
Is it possible? or any other better solutions?
Whenever you need an isolated set of users for an app, you will need a new project for that app. You can use multiple databases per project following the instructions in this article (it is for Android, but it's similar for iOS -
you will have to initialize a new Firebase app in the client for each project you want to use).
After several hours of study, I come up with other approach. The idea is:
Use Facebook iOS sdk to sign in from iOS app and get Facebook token.
iOS app sends this token to cloud functions, fetch user profile using Graph api, then create custom token from Facebook uid.
Send this custom token back to iOS app.
iOS app uses this token to sign in to firebase.
iOS code :
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if let token = result.token {
print(token.userID)
print(token.appID)
signInuser(with: token)
}
}
func signInuser(with token:FBSDKAccessToken) {
Alamofire.request("https://xxx.cloudfunctions.net/verifyFacebookUser", method: .post, parameters: ["token":token.tokenString]).responseJSON(completionHandler: { (response) in
switch response.result {
case .success(let data):
if let json = data as? [String:String] {
FIRAuth.auth()?.signIn(withCustomToken: json["token"]!, completion: { (user, err) in
if let error = err {
print(error)
}else {
print(user!.displayName)
print(user!.email)
}
})
}
case .failure( let error):
print(error)
}
})
}
cloud functions:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const graph = require('fbgraph');
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://xxx.firebaseio.com"
});
exports.verifyFacebookUser = functions.https.onRequest((req,res) => {
if (!req.body.token) {
return res.status(400).send('Access Token not found');
}
graph.setAccessToken(req.body.token);
graph.get("me?fields=id,name,email", function(err, result) {
const firebaseUid = "fb:"+result.id;
admin.auth().createUser({
uid:firebaseUid,
displayName:result.name,
email:result.email
}).then(function(userRecord){
console.log(userRecord)
admin.auth().createCustomToken(userRecord.uid)
.then(function(customToken) {
res.send({token:customToken});
})
.catch(function(error) {
console.log("Error creating custom token:", error);
})
});
});
});
With this method, the iOS app of each brand will ask user to agree sign in from Facebook even if he or she already sign in from different brand app. However that means iOS app needs to implement Facebook native sign in process which Firebase SDK already provide.

Offline sync with Azure App Service

I try to upgrade an objective-c project to Swift. I'm using Azure App Service with a .NET backend to store data from my mobile app (iOS) in the cloud. I just downloaded the quickstart for Swift project from the azure portal and followed the steps in the tutorial to enable offline sync functionality. However inserting an item in the table is not working. I am using the following code to store a new item in the backend
var table : MSSyncTable?
...
self.table!.insert(item) {
(result) in
let syncItem = result.0
let error = result.1
if error != nil {
print("Error: " + error!.localizedDescription)
}
...
}
Stepping through the code at runtime revealed that error is nil so everything should be working fine, but I am not getting a new entry in my table storage.
Does anybody have experience with Azure App Service and Swift and can help me with this?
Because you are using the sync table, the actual operations to send and receive data from the server are explicit. These are represented by the pushWithCompletion:error: method on the sync context (for sending data up to the cloud), and the pullWithQuery:query:queryId:completion: method on your MSSyncTable.
Note that push automatically occurs when you pull as well.
I would expect the code to look something like:
var table : MSSyncTable?
...
self.table!.insert(item) { result in
let syncItem = result.0
let error = result.1
if error != nil {
print("Error: " + error!.localizedDescription)
}
table!.pushWithCompletion() { error in
...
}
...
}

How to send iOS data to Azure mobile service database?

I'm new to swift and Azure and am trying to send some data from my swift app up to my azure mobile database. I have an app with CoreData working but after fetching my data i'm running into difficulties sending it up to azure. I'm trying to use the insert table method from the azure framework.
I've tried this method:
let client = MSClient(applicationURLString: "https://mymobileapp.azure-mobile.net/", applicationKey: "aAaBbBcCc…")
var client = AppDelegate().client // To reference my constant in AppDelegate.swift
var itemTable:MSTable = client.tableWithName("Item")
var itemToInsert:NSDictionary = ["text":"My Awesome Item 1"]
itemTable.insert(itemToInsert,
completion: {
insertedItem, error in
if error{
println("error: \(error)")
}
else{
println("Success!")
}
}
)
But I'm running into problems with the application key. From what I can gather, the application keys are no longer used in Azure Mobile Apps.
I've also tried the method shown in the Mobile App QuickStart guide for swift but the code seems to be for an older version of swift.
I'm not trying to display a table in my app just upload data to the database. Any help would be appreciated!
I ended up getting it to work. The application key is no longer used in the new Azure Mobile Apps. In addition to deleting the key you have to add a new property, specifically a App Transport Security property, to allow for a connection with an unsecured HTTP site.
let client = MSClient(applicationURLString: "https://mymobileapp.azure-mobile.net/")
var client = AppDelegate().client // To reference my constant in AppDelegate.swift
var itemTable:MSTable = client.tableWithName("Item")
var itemToInsert:NSDictionary = ["text":"My Awesome Item 1"]
itemTable.insert(itemToInsert,
completion: {
insertedItem, error in
if error{
print("error: \(error)")
}
else{
print("Success!")
}
}
)

Resources