How to check if the logged-in Realm user logged in via "Sign in with Apple"? - swift

The alternative title is "How to check the logged-in Realm user logged in via certain authentication provider?" OR "How to check a user is using a specific authentication provider/method?"
For an app start with an anonymous user and then linked to another authentication provider using user.linkUser(credentials: credential). Since the user always has a value either an anonymous user or a linked user.
How can I know if the current logged-in user is already linked with another auth provider e.g. "Sign in with Apple" or "Google"? This information needs to be known in order to hide the auth provider sign-in button.

In RealmSwift 10.12.0
There is an identifiers property under user. It is an array of RLMUserIdentity. the user identity contains a providerType string, https://docs.mongodb.com/realm-sdks/objc/latest/Classes/RLMUserIdentity.html#/c:objc(cs)RLMUserIdentity(py)providerType
Below is a sample output
print(">>> DEBUG:", user.identities.map { identity in (identity.identifier, identity.providerType)
[("611a27f9a1575af5ed15234e-lnnaeteekatdftrnsmpbpldr", "anon-user"), ("000766.23cbd125344c140b18ef0baa4deccaf32.61234", "oauth2-apple")]
Now you can check if the user identities contains the provider you care and hide the "sign in" button/link for that provider
https://github.com/realm/realm-cocoa/blob/d407cdc1c8be5f04c3decd37b88524855edfa7e8/Realm/RLMCredentials.mm

When you initialize a realm app, it checks by default for the accessToken, refresh token and other stuff that Realm does to store user data on the device after successful login. So, the default value when you declare your current user should be retrieved from Realm app instance. In my case, I use web development and it looks like this for React application.
import React, { useState } from 'react';
import LoginPage from 'containers/LoginPage';
import { RealmApp, getCustomCredentials } from './RealmApp';
function App() {
const [currentUser, setCurrentUser] = useState(RealmApp.currentUser);
const onAuth = async (data) => {
const credentials = getCustomCredentials(data);
const user = await RealmApp.logIn(credentials);
setCurrentUser(user);
};
const App = () => {
return (
<Box className="app-root-component" sx={{ display: 'flex' }}>
<h1>App</>
</Box>
);
};
return currentUser ? <App /> : <LoginPage onAuth={onAuth} />;
}
export default App;
here I have my variable & it's setter function
const [currentUser, setCurrentUser] = useState(RealmApp.currentUser);
I wish that helps you to get closer with apple sign in

Related

Detect if user is connected to firebase app

I want to implement an "isActive" feature on my app that is built using firebase firestore. I am using firebase functions along with authentication in my React App.
Is there a way of detecting if the user is "active" or "inActive" on my app by triggering a cloud function when they login or disconnect to the app?
If i can determine this i would store the value in firestore and handle it in the frontend to display a UI.
Thanks
I see two aspects to the question here:
Auth state: You want to track the logged-in duration of the user.
Focus state: You want to track when the user is active on the app.
For #1, you will have to listen to the auth state changes, and you may also want to change your auth state persistence strategy accordingly. From https://firebase.google.com/docs/auth/web/auth-state-persistence:
Enum
Value
Description
firebase.auth.Auth.Persistence.LOCAL
'local'
Indicates that the state will be persisted even when the browser window is closed or the activity is destroyed in React Native. An explicit sign out is needed to clear that state. Note that Firebase Auth web sessions are single host origin and will be persisted for a single domain only.
firebase.auth.Auth.Persistence.SESSION
'session'
Indicates that the state will only persist in the current session or tab, and will be cleared when the tab or window in which the user authenticated is closed. Applies only to web apps.
firebase.auth.Auth.Persistence.NONE
'none'
Indicates that the state will only be stored in memory and will be cleared when the window or activity is refreshed.
import { getAuth, setPersistence, signInWithRedirect, inMemoryPersistence, GoogleAuthProvider } from "firebase/auth";
const auth = getAuth();
setPersistence(auth, inMemoryPersistence)
.then(() => {
const provider = new GoogleAuthProvider();
// In memory persistence will be applied to the signed in Google user
// even though the persistence was set to 'none' and a page redirect
// occurred.
return signInWithRedirect(auth, provider);
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
});
You don't need a cloud function for th
For #2, you might want to check out this blog. https://javascript.plainenglish.io/validate-your-apps-session-on-focus-892f610f7e23.
import { useLocation } from '#reach/router';
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
export function WindowFocusHandler() {
const dispatch = useDispatch()
const location = useLocation()
useEffect(() => {
window.addEventListener("focus", onFocus)
return () => {
window.removeEventListener("focus", onFocus)
}
}, [])
const onFocus = () => dispatch(session.effects.checkSessionOnFocus(location))
return <></>
}
The Focus event on a window might get triggered more often if it is a browser app. Depending on what you want to achieve, both things should be possible in
your front-end code without the need for cloud functions. Although, you might opt to code the logic as a firebase function and invoke it from your react code when onFocus.

Keycloak-js perform action after user authentication

I'm using Keycloak for my auth server in a reactJs site. I have all the normal workflows working using keycloak-js, KeycloakProvider and useKeycloak. I need to have some custom redirection rules however when a user logs in based on their role. i.e.
If they log in from the home screen, then
a) If it is a normal user they are redirected to /dashboard
b) If it is an admin user they are redirected to /admin
If they tried to reach some protected screen, they should be redirected back to the screen they requested
Right now it keycloak always directs them to the last screen they were on and I don't see a way to capture the "on intial login" event.
Any suggestions?
Maybe it's sufficient to check for the roles after the init/login or alternatively the keycloak.idTokenParsed.preferred_username and then redirect after a successfuly auth to your desired path (eg React Router or history.push("/dashboard") (with the useHistory hook))?
What I ended up doing is creating a /login route that my login points to. The only thing it does is force authentication the redirect to the proper page.
const RoleRedirector = (params) => {
const { keycloak,initialized } = useKeycloak();
const history = useHistory();
useEffect(()=>{
if(keycloak && initialized){
if(keycloak.authenticated){
const { tokenParsed } = keycloak;
const { realm_access } = tokenParsed;
const { roles } = realm_access || [];
if(roles.includes("admin")) {
//This is a separate React site living under the common domain
document.location = "/admin/index.html";
}else if(roles.includes("manager")) {
document.location = "/manager";
}else {
history.push("/dashboard");
}
}else{
keycloak.login();
}
}
},[keycloak,initialized])
return <div/>
}

How to get auth code for Google OAuth using the Mongo Stitch React-Native SDK?

From the docs it seems like there is no other way to sign-in using Google other than using the GoogleCredential constructor which takes an authCode as a mandatory parameter, how should I get it?
For an example of [[loginWithRedirect]], see Facebook Authentication
Also, there are multiple references in the docs to a function called loginWithRedirect, but they don't link anywhere and there is no property in the auth object called loginWithRedirect.
Indeed, the RN and server SDKs do not support the redirect concept. You have to get your own authCode.
Stitch's GoogleCredential constructor just expects a valid server auth code so that the Stitch service can use offline access.
Use a third-party OAuth module
I had no luck using the official google-auth-library SDK with RN. I was able to make it work (on iOS, at least -- haven't tried Android, yet) with react-native-google-signin from react-native-community. The installation process is a bit involved, so be sure to follow their instructions carefully!
I will show how I used this specific library to sign in. Hopefully this information can be applied to other OAuth libraries and other Authentication providers (e.g. Facebook).
Configure GoogleSignin
The webClientId must be specified and must match the Client ID under the Google Oauth2 configuration on the Stitch UI (see screenshot). The iosClientId is found in the GoogleService-Info.plist you download after following these steps. Finally, set offlineAccess to true.
If you use the Google iOS SDK directly or another library, note that webClientId is called serverClientID and iosClientId is simply called clientId.
Here's my configure code (see my complete App.js file):
componentDidMount() {
// ...
GoogleSignin.configure({
webClientId: '<id>', // from Stitch UI > Users > Providers > Google
offlineAccess: true,
iosClientId: '<id>', // CLIENT_ID in GoogleService-Info.plist
});
}
Render GoogleSigninButton
react-native-google-signin provides a nice button to use, which I rendered out (see screenshot):
const loginButton = <GoogleSigninButton
style={{ width: 192, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={this._onPressLogin}
disabled={this.state.isSigninInProgress}
/>
Give Stitch the serverAuthCode from GoogleSignin
My _onPressLogin function uses GoogleSignin to get the serverAuthCode. It then passes that code to Stitch:
_onPressLogin = async () => {
// They recommend calling this before signIn
await GoogleSignin.hasPlayServices();
// Call signIn to get userInfo
const userInfo = await GoogleSignin.signIn();
// Check if serverAuthCode was received -- it will be null
// if something wasn't configured correctly. Be sure to
// log out after changing a configuration.
const {serverAuthCode} = userInfo;
if (serverAuthCode === null) {
throw new Error('Failed to get serverAuthCode!');
}
try {
// Pass serverAuthCode to Stitch via GoogleCredential
const user = await this.state.client.auth.loginWithCredential(new GoogleCredential(serverAuthCode));
console.log(`Successfully logged in as user ${user.id}`);
this.setState({ currentUserId: user.id });
} catch(err) {
console.error(`Failed to log in anonymously: ${err}`);
this.setState({ currentUserId: undefined })
}
Logging out
I found I had to log out several times while testing (and figuring out which client IDs to use where), or else serverAuthCode would come back null. It was good to have the logout button visible at all times. My logout code looks like this:
_onPressLogout = async () => {
await GoogleSignin.revokeAccess();
await GoogleSignin.signOut();
const user = await this.state.client.auth.logout();
console.log(`Successfully logged out`);
this.setState({ currentUserId: undefined })
}
I hope this helps!

How do you navigate to a user profile with Google uid without showing it in the url

I was just wondering how to navigate to a users profile with their google uid in ui-router. I'm assuming that this is sensitive information that shouldn't be displayed but I'm not really too sure so I figured I would ask to make sure I'm doing things the right way.
The link I am using is:
<a ui-sref="timeline.profile({ userKey: post.userKey })" ui-sref-active="selected">{{post.userKey}}</a>
Where userKey is equal to the google uid. And then this is my state for the public profile.
.state('timeline.profile', {
url: '/{userKey}',
views: {
'timeline-tab#tab': {
templateUrl: 'timeline/templates/public-profile.html',
controller: 'PublicProfileCtrl as publicProfileCtrl'
}
},
resolve: {
auth: function($state, Auth){
return Auth.$requireAuth().catch(function(){
});
},
publicProfile: function(Users, Auth, $stateParams) {
return Auth.$requireAuth().then(function(authData) {
return Users.getPublicProfile($stateParams.userKey).$loaded();
});
}
}
})
It works for grabbing the users profile and navigating to the template but the url contains the google uid. I was just wondering how to achieve the same result without showing the google uid if that is sensitive information that shouldn't be shown.
And I was also wondering if this was good practice to create two different profiles. A private profile where the user can access all of their information and then a public profile that just displays the information.

get user images twitter | angular.js | firebase

im quite new to angular.js and firebase, so im starting to edit some code from an open source script to expens my knowledge ... i used a chat script with a facebook login.
i decided to go change the facebook login to a twitter login. (firebase lets you use logins pretty easy)
function onLoginButtonClicked() {
auth.login("Twitter");
}
the code also automaticly gets the user image from facebook with
<div id="comments">
<script id="template" type="text/template">
<img class="pic" src="https://graph.facebook.com/{{userid}}/picture">
<span><b>{{name}}</b><br/>{{body}}</span>
</script>
But now i changed it to an twitter app i wonder how i can get the twitter user icons instead?
--edit--
whats wrong with the question?
If you check out the user info returned from the login process, you'll see that it contains a an object called thirdPartyUserData. It contains all of the information provided by twitter during login; this is their purview and could change when their API or policies change in the future, but has (for as long as I've been familiar with the Twitter API) contained URLs for user's avatars:
var ref = new Firebase(URL);
var auth = new FirebaseSimpleLogin(ref, function(err, user) {
if( err ) console.error(err);
console.log('avatar is ', user && user.thirdPartyData.profile_image_url);
});
$('button').click(function() {
console.log('clicked it');
auth.login('twitter');
});
(Side note: the login provider is twitter vs Twitter)
There is another way to get the Twitter avatar which works better since getting it from the login user object is only for the logged in user and so would require that the URLs be saved which would then be a problem if the user ever changed their twitter avatar since the URL would then be missing. After some searching around I found that the twitter avatar (or facebook) avatar can be reached from the firebase user id as follows:
var info = userId.split(':');
var provider = info[0];
var id = info[1];
if ( provider === 'facebook' ) {
return 'https://graph.facebook.com/' + id + '/picture?type=square';
} else if ( provider === 'twitter' ) {
return 'http://twitter.com/api/users/profile_image/' + id + '?size=normal';
}