Adding “Sign In with Apple” in Expo react native app that uses Firebase auth - react-native

I have a react native app that uses Expo (managed, not detached) and that uses Firebase auth to provide Facebook login and email/password login.
I now need to implement “Sign in with Apple” as per Apple’s new rules.
Expo provides a way to do this, and it works, returning the user’s info. But because all users are managed through Firebase auth, I need to take what Apple sends me and pass it to Firebase auth.
The Firebase docs explain how to do this using signInWithCustomToken. But that requires that I create the token. In Node this would be simple, but this app is serverless and I haven’t found a tool that can generate an RS256 token on the client. From the Firebase docs it seems that RS256 is a requirement. I’ve tried using expo-jwt with HS256 and Firebase returns an error that the token is badly formed. But besides using HS256 instead of RS256 I see no other possible problems. The token is encoded and decoded successfully as follows.
const appleJwt = JWT.encode(
{
familyName: 'M',
givenName: 'Greg',
email: 'apple_user#example.com',
alg: 'HS256',
iss:
'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
sub: serviceAccountEmail,
aud: serviceAccountEmail,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600,
uid: appleUid
},
key,
{
algorithm: 'HS256'
}
);
console.log('TCL: loginWithApple -> appleJwt', appleJwt);
const appleJwtDecoded = JWT.decode(appleJwt, key);
console.log('TCL: loginWithApple -> appleJwtDecoded', appleJwtDecoded);
It’s only when I try to use it with Firebase auth that it returns an error that the token is badly formatted.
return Firebase.auth().signInWithCustomToken(appleJwt).then(...
Note that the key and the serviceAccountEmail were retrieved from the firebase console.
I’m wondering if perhaps there’s some simpler solution that I’m overlooking. The community is awaiting word from Firebase on if they’ll provide out of the box login with Apple, like they do for other providers, so maybe I just need to be patient. But I’d prefer to find a solution.
A big thanks in advance for any advice.
Update 2019-10-15
I built a simple node server with an API that my app could use to generate the token with RS256, but Firebase still responds that the token is badly formatted when I pass it to signInWithCustomToken. Can’t see what’s wrong with it.
So, since I had the node server built, I just configured the Firebase Admin SDK and used the provided createCustomToken to generate the token. Firebase accepts it now when I pass it to signInWithCustomToken, which was my problem, so this issue is settled for me. After the custom Firebase sign in succeeds the first time, I write all the user data to Firestore. For subsequent sign ins, it just updates the last login date in Firestore. Hopefully Firebase will still provide their own solution soon too, since having a separate node server just for this is not ideal.

Firebase Auth now supports Apple sign in across all 3 platforms:
https://firebase.google.com/docs/auth/ios/apple
https://firebase.google.com/docs/auth/android/apple
https://firebase.google.com/docs/auth/web/apple

The Firebase team is working on implementing this in the official sdk
https://github.com/firebase/firebase-ios-sdk/issues/3145#issuecomment-510178359

Related

How to achieve the Dropbox equivalent of long-lived token now that they're gone (dropbox-sdk-js, Meteor, React)

For a while now I've been using dropbopx-sdk-js in a Meteor application without any trouble.
My Meteor app simply uses Dropbox to fetch images to be used in product cards. These files are synced now and then and that's it. By synced what I mean is they are scanned, shared links created or obtained, and some info is then saved in Mongo (name, extension, path, public link)
End users do not remove nor add files, nor are the files related to an end user specific account.
To achieve this, I created (in the far past) an App in the Dropbox App Console, generated a permanent token, and used that token in my Meteor app to handle all the syncing.
Now I've tried to replicate that very same thing in a new similar project, but found that the permanent tokens have been recently deprecated and are no longer an option.
Now, checking Dropbox's Authentication Types it seems to me like "App Authentication"
"This type only uses the app's own app key and secret, and doesn't
identify a specific user or team".
is what I'm after. I can safely provide app key and secret in the server exclusively, as the client will never need those. The question is how do I achieve such kind of authentication? Or for that matter, how do I achieve an equivalent of the long-lived token for my app, ultimately meaning that end users don't actually need to know Dropbox is behind the scenes in any way (and they surely don't need dropbox accounts to use this app nor should be prompted with any Dropbox authentication page)
In the js-sdk examples repo, I only found this example using app key and secret. Yet afterwards it goes through the oauth process in the browser anyways. If I don't do the oauth part, I get an error
"error": {
"name": "DropboxResponseError",
"status": 409,
"headers": {},
"error": {
"error_summary": "path/unsupported_content_type/...",
"error": {
".tag": "path",
"path": {
".tag": "unsupported_content_type"
}
}
}
}
as a result of calling
dbx.filesListFolders({ path: '', recursive: true }):
If I replace the initialization of the dbx object with a generated token everything works out. However eventually the token expires and I'm back in square one.
Any ideas what may I be missing?
The short answer is:
You need to obtain a refresh-token. You can then use this token for as long as you want. But in order to get it is necessary to go through at least one oauth flow in the browser. Then capturing the generated refresh-token in the backend. Then store it and use it to initialize the API. So it's kind of "hacky" (IMO).
For example, you can use the mentioned example code, and log/store the obtained refresh token in this line (as per Greg's accepted answer in the forum). Then use that value as a constant to immediately call the setRefreshToken method (as done in that very same line) upon initialization.
The long answer is:
ClientId + Client secret are not enough to programmatically generate a refresh token.
Going through the oauth flow at least once is mandatory to obtain a refresh token
If you want to hide such flow from your clients, you'll need to do what the short answer says.
The intended flow of usage according to Dropbox is: each user access his own files. Having several users accessing a single folder is not officially supported.
The longer answer is:
Check out the conversation we had in the dropbox forum
I suggested to replace the "Generate Access Token" button in the console for a "Generate Refresh Token" button instead. At least it made sense to me according to what we discussed. Maybe if it gets some likes... ;).

React Native Logging in to Web Page

Can someone point me in the right direction on how I could go about writing a piece of code for react native that takes an input from the user (username and password) and then logins into a website with in.
Also any react native web scraping libraries anyone could recommend (I was thinking about using Cheerio)
Thanks in Advance :)
For getting user inputtext check:
https://facebook.github.io/react-native/docs/textinput
https://www.tutorialspoint.com/react_native/react_native_text_input.htm
From now on what i'm saying works in restful APIs and you may need to change your approach based on your need.
In my case being logged in means having a valid token from server.
For sending requests use axios. Get your user input values and make a post request to login api. If credentials were valid response will contain a token. Save that token in your app using Asyncstorage.
For logging out delete that token from storage.

Is it possible to use MSAL.js to get refresh token?

I want to integrate with Miscrosoft Outlook. I am able to login with MSAL.js and get an access token, but I am not able to get a refresh token. Is there a way to do it?
I'll assume that since you're using the MSAL.js (https://github.com/AzureAD/microsoft-authentication-library-for-js) that you're using implicit flow for authentication and authorization.
Implicit flow doesn't support refresh tokens, but you can request a new token silently. This is done similarly to how you request the token (id or access) in the first place. Unfortunately, I haven't found that MSAL.js does this transparently and I've needed to detect expired tokens and request the new tokens in my code. You can read more about refreshing tokens here.
Alternatively, if what you're implementing allows you to use one of the other MSAL libraries (for example, the .Net one) then you can use one of the other OAuth flows that explicitly support refresh tokens.
I couldn't find any answer in the MSAL.js documentation, however this source code comment suggests you can renew a token manually by passing only the clientId as your scope to acquireTokenSilent.
To renew idToken, please pass clientId as the only scope in the Authentication Parameters
I use msal v1.4.0
I remove 2 keys in storage (see picture) then call acquireTokenSilent again to get new access token.
Code to remove those 2 keys:
const keys = Object.keys(sessionStorage).filter(x => x.indexOf('authority') > 0)
keys.forEach(x => sessionStorage.removeItem(x))

Is AsyncStorage secure?

I'd like to persist a user's account credentials (i.e. username and password) in my React Native app. Should I use AsyncStorage?
In other words, I want to know if and how AsyncStorage protects its contents. The docs are silent on that.
(I'm using RN v0.28)
Is AsyncStorage secure?
No AsyncStorage is not secure, the docs says:
AsyncStorage is a simple, unencrypted, asynchronous, persistent,
key-value storage system that is global to the app. It should be used
instead of LocalStorage.
To store secure information on the native side, I really recommand you to use react-native-keychain with react-native
For iOS it use Keychain Sharing Capabilities
For Android it use:
API level 16-22 use Facebook Conceal
API level 23+ use Android Keystore
This is a simple example:
// Generic Password, service argument optional
Keychain
.setGenericPassword(username, password)
.then(function() {
console.log('Credentials saved successfully!');
});
// service argument optional
Keychain
.getGenericPassword()
.then(function(credentials) {
console.log('Credentials successfully loaded for user ' + credentials.username);
}).catch(function(error) {
console.log('Keychain couldn\'t be accessed! Maybe no value set?', error);
});
If you are using Expo sdk, you can use SecureStore for sensitive information.
NO (at least on iOS, RN v0.28)
AsyncStorage saves key-value pairs as a plaintext JSON file in the Documents directory.
If you run it in the iOS Simulator, you can find its contents on ~/Library/Developer/CoreSimulator/Devices
Should have been obvious from the source code for RCTAsyncLocalStorage
You should NEVER save the username and password in plain text in client applications. Please note, never save sensitive data in plain text. You should use a token to authenticate the user.
Regarding the security of the AsyncStorage read this answer. TL;DR the data is safe unless the attacker have access to the device or the device is rooted(android)/jailbroken(iOS). The data is not encrypted. So, with root or physical access to the device (and the device is not protected) it is possible to access to that data.

Access token Facebook API Graph - public events

I have a general quation about the access token for Facebook API Graph.
I'm doing some testing with the API Graph explorer. I want to display some public facebook events on my website. https://graph.facebook.com/search?q=party&type=event
For the testing I created a access token and it's working.
Now my question: Can I use this token for my website or has it to be renewed all x-days? So when only searching for public events this token is good?
Thanks for your help.
Ben
You should be able to use an App Access Token, it is valid forever. It´s just a combination of App ID and App Secret, including a pipe sign: App-ID|App-Secret
Careful though, don´t use that token on the client.
More information:
https://developers.facebook.com/docs/facebook-login/access-tokens
http://www.devils-heaven.com/facebook-access-tokens/
Edit: I just tested it with an App Token, it seems that you do need a User Token after all. You can use an Extended User Token, it is valid for 60 days.