Firebase: How can i use onDisconnect during logout? - angularjs

How can i detect when a user logs out of firebase (either facebook, google or password) and trigger the onDisconnect method in the firebase presence system. .unauth() is not working. I would like to show a users online and offline status when they login and out, minimize the app (idle) - not just when the power off their device and remove the app from active applications on the device.
I'm using firebase simple login for angularjs/ angularfire
Im using code based off of this tutorial on the firebase site.
https://www.firebase.com/blog/2013-06-17-howto-build-a-presence-system.html
Please i need help with this!
Presence code:
var connectedRef = new Firebase(fb_connections);
var presenceRef = new Firebase(fb_url + 'presence/');
var presenceUserRef = new Firebase(fb_url + 'presence/'+ userID + '/status');
var currentUserPresenceRef = new Firebase(fb_url + 'users/'+ userID + '/status');
connectedRef.on("value", function(isOnline) {
if (isOnline.val()) {
// If we lose our internet connection, we want ourselves removed from the list.
presenceUserRef.onDisconnect().remove();
currentUserPresenceRef.onDisconnect().set("<span class='balanced'>ā˜†</span>");
// Set our initial online status.
presenceUserRef.set("<span class='balanced'>ā˜…</span>");
currentUserPresenceRef.set("<span class='balanced'>ā˜…</span>");
}
});
Logout function:
var ref = new Firebase(fb_url);
var usersRef = ref.child('users');
service.logout = function(loginData) {
ref.unauth();
//Firebase.goOffline(); //not working
loggedIn = false;
seedUser = {};
clearLoginFromStorage();
saveLoginToStorage();
auth.logout();
};

The onDisconnect() code that you provide, will run automatically on the Firebase servers when the connection to the client is lost. To force the client to disconnect, you can call Firebase.goOffline().
Note that calling unauth() will simply sign the user out from the Firebase connection. It does not disconnect, since there might be data that the user still has access to.
Update
This works for me:
var fb_url = 'https://yours.firebaseio.com/';
var ref = new Firebase(fb_url);
function connect() {
Firebase.goOnline();
ref.authAnonymously(function(error, authData) {
if (!error) {
ref.child(authData.uid).set(true);
ref.child(authData.uid).onDisconnect().remove();
}
});
setTimeout(disconnect, 5000);
}
function disconnect() {
ref.unauth();
Firebase.goOffline();
setTimeout(connect, 5000);
}
connect();

Related

Correct Flow for Google OAuth2 with PKCE through Client App to SAAS API Server

So we are working on a client application in Windows WPF. We want to include Google as a login option and intend to go straight to the current most secure method. At the moment we have spawned a web browser with the following methods to obtain a Authorization Code
private async void HandleGoogleLogin() {
State.Token = null;
var scopes = new string[] { "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "openid" };
var request = GoogleOAuthRequest.BuildLoopbackRequest(scopes);
var listener = new HttpListener();
listener.Prefixes.Add(request.RedirectUri);
listener.Start();
// note: add a reference to System.Windows.Presentation and a 'using System.Windows.Threading' for this to compile
await Dispatcher.Invoke(async () => {
googleLoginBrowser.Address = request.AuthorizationRequestUri;
});
// here, we'll wait for redirection from our hosted webbrowser
var context = await listener.GetContextAsync();
// browser has navigated to our small http servern answer anything here
string html = string.Format("<html><body></body></html>");
var buffer = Encoding.UTF8.GetBytes(html);
context.Response.ContentLength64 = buffer.Length;
var stream = context.Response.OutputStream;
var responseTask = stream.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
stream.Close();
listener.Stop();
});
string error = context.Request.QueryString["error"];
if (error != null)
return;
string state = context.Request.QueryString["state"];
if (state != request.State)
return;
string code = context.Request.QueryString["code"];
await APIController.GoogleLogin(request, code, (success, resultObject) => {
if (!success) {
//Handle all request errors (username already exists, email already exists, etc)
} else {
((App)Application.Current).UserSettings.Email = resultObject["email"].ToString();
((App)Application.Current).SaveSettings();
}
attemptingLogin = false;
});
}
and
public static GoogleOAuthRequest BuildLoopbackRequest(params string[] scopes) {
var request = new GoogleOAuthRequest {
CodeVerifier = RandomDataBase64Url(32),
Scopes = scopes
};
string codeChallenge = Base64UrlEncodeNoPadding(Sha256(request.CodeVerifier));
const string codeChallengeMethod = "S256";
string scope = BuildScopes(scopes);
request.RedirectUri = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
request.State = RandomDataBase64Url(32);
request.AuthorizationRequestUri = string.Format("{0}?response_type=code&scope=openid%20profile{6}&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
AuthorizationEndpoint,
Uri.EscapeDataString(request.RedirectUri),
ClientId,
request.State,
codeChallenge,
codeChallengeMethod,
scope);
return request;
}
To my understanding, from this point the client app has completed the required portion to have the user login to their google account and approve any additional privileges.
Our API/App server is in GoLang.
APIController.GoogleLogin
from above sends the CodeVerifier and AuthorizationCode to the GoLang application server to then finish off the OAuth2 Flow.
Is this the correct flow given our client-server setup?
If so, what is the best practice for the Go Server to retrieve a Access Token/Refresh Token and get user information? Should the client app be performing a looping check-in to the app server as the app server will not immediately have the required information to login?
Thanks for the help!

AWS Cognito Authentication in Reactjs

I have built applications using Firebase and React, and the procedure is pretty seamless.
Lately I have been required to use AWS Cognito, and it seems a bit of a pain to set up as the docs are not clear.
Firstly, how can I do user authentication using Cognito? I set up a user pool, with the following app client settings:
Now, I add the authorizer to my API as follows:
Now my question is, how do I use this with my frontend to sign in a user and make authenticated API calls?
There seem to be two different toolkits available:
https://github.com/aws/aws-sdk-js
https://github.com/aws-amplify/amplify-js
It is not clear at all for a beginner what to use, and how to get authentication working. Ideally I would use it like I do for firebase, and just have my frontend make an authentication call using the email and password, and in turn receiving a token of some sort (on success only) that can then be used to then make signed API calls.
Can someone please help with code examples?
sorry for the confusion.
AWS Cognito Userpools act as an Identity Provider. It supports all User management (Sign Up, Sign In, Password reset, User deletion, etc). Cognito also supports Federated Identity (E.g., A User who already has an Google/Facebook account can sign in). In this case, Cognito talks to Google/Facebook using OAuth.
When I was learning about Cognito/JWT tokens, I created a simple JS/HTML to understand how it works. Since you asked for code, you can refer it - https://github.com/ryandam9/Cognito-tokens.
As per your screen shot, you already configured a Userpool - sls-notes-backend. Say, you configured the mandatory attribute as Email.
Step 0 - Initialize
You get both userPoolId and appId when you create the user pool.
poolData = {
UserPoolId: userPoolId,
ClientId: appId
};
userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
Step 1 - Signup a User using Email and Password - Say your UI already captured these details from the User and user clicked on 'Sign Up' button.
/**
* Signup a User
* #param e
*/
function addUser(e) {
signupMessage.style.display = 'none';
signupMessage.className = '';
e.preventDefault();
let name = document.getElementById('name').value.trim();
let email = document.getElementById('signup-email').value.trim();
let password = document.getElementById('signup-password').value.trim();
if (name.length === 0 || email === 0 || password === 0) {
return;
}
let attributeList = [
new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({
Name: 'given_name', Value: name
}),
];
userPool.signUp(email, password, attributeList, null, function (err, result) {
if (err) {
signupMessage.innerText = err;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-danger';
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
// Show a text box to enter Confirmation code
document.getElementById('signup-btn').style.display = 'none';
document.getElementById('code-block').style.display = 'block';
document.getElementById('confirm-user-btn').style.display = 'inline-block';
});
}
If the signup is successful (It is valid Email and the Email does not yet exist in Userpools, a Confirmation code is sent to the Email provided. Next step is to, allow the user to enter the code and confirm his identity.
Step 3 - Confirm User
/**
* Confirm the user by taking the Confirmation code.
* #param e
*/
function confirmUser(e) {
e.preventDefault();
let verificationCode = document.getElementById('code').value;
cognitoUser.confirmRegistration(verificationCode, true, function (err, result) {
if (err) {
signupMessage.innerText = err;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-danger';
return;
}
signupMessage.innerText = result;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-success';
});
}
If the User enters correct code, his identity is confirmed. At this point, An entry is made to the Userpool for this user. It looks like this.
Step 4 - Authentication (Sign In)
At this point, User registration is done. Its time to allow him to login. Please ignore the unnecessary code in the code below (the code that fetches and prints credentials, decoding part). If the authentication is successful, Cognito returns two types of Tokens to the application - ID Token and Access Token. These are valid only for this session and for this user only. More details here - https://ryandam.net/aws/19-cognito-userpools/index.html#0
/**
* Signin user with Email and Password
* #param e
*/
function authenticateUser(e) {
e.preventDefault();
let email = document.getElementById('signin-email').value;
let password = document.getElementById('signin-password').value;
if (email.length === 0 || password === 0 || userPool === null || userPool === undefined) {
signinMessage.innerText = 'Fill in all fields!';
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-danger';
return;
}
let authenticationData = {
Username: email,
Password: password,
};
let authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
let userData = {
Username: email,
Pool: userPool
};
let cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
signinMessage.innerText = 'Authentication Success!';
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-success';
document.getElementById('token-section').style.display = 'block';
document.getElementById('signin-btn').style.display = 'none';
// Decode ID Token
let idToken = result.idToken.jwtToken;
document.getElementById('id-token').innerText = idToken;
document.getElementById('decoded-id-token').appendChild(parseIdToken(idToken));
// Decode Access Token
let accessToken = result.getAccessToken().getJwtToken();
document.getElementById('access-token').innerText = accessToken;
document.getElementById('decoded-access-token').appendChild(parseAccessToken(accessToken));
let cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function (err, result) {
if (result) {
// Set the region where your identity pool exists (us-east-1, eu-west-1)
AWS.config.region = region;
AWS.config.update({region: region});
logins = {};
let key = 'cognito-idp.us-east-2.amazonaws.com/' + userPoolId;
logins[key] = result.getIdToken().getJwtToken();
// Add the User's Id Token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: identityPoolId,
Logins: logins,
});
// Make the call to obtain credentials
AWS.config.credentials.get(function () {
// Credentials will be available when this function is called.
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
});
if (s3BucketName.length > 0)
listS3Bucket(s3BucketName);
}
});
}
},
onFailure: function (err) {
signinMessage.innerText = err;
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-danger';
}
}
);
}
Step 5 - Invoking the API Endpoint you already created - Since you've already created an Authorizer using the Userpool and you're using Authorization as the header, you can invoke the End point from JS by passing the ID token as Authorization header. What happens is that, the token is validated by the authorizer. Since it is valid the user is able to invoke the API.**
I do not have JS code, you can test your API from CLI/Postman something like this:
Note
AWS Amplify seems to be a wrapper for Cognito and other services. For instance, Amplify sets up User pool for you when you invoke its CLI commands. You can refer this code if you want to see how Amplify works with Flutter - https://github.com/ryandam9/Auth-flutter-aws-amplify.
I am still learning. I tried to be as accurate as possible.

IdentityServer4 Windows Authentication Missing Callback implementation

The documentation to setup Windows Authentication is here: https://docs.identityserver.io/en/latest/topics/windows.html
But I have no idea how to configure the Callback() method referred to in the line RedirectUri = Url.Action("Callback"), or wethere or not I'm even supposed to use that.
I tried manually redirecting back to the https://<client:port>/auth-callback route of my angular app but I get the error:
Error: No state in response
at UserManager.processSigninResponse (oidc-client.js:8308)
Does someone have a suggested Callback method I can use with an SPA using code + pkce ? I've tried searching Google but there are no current example apps using Windows Authentication and the ones that do exist are old.
Take a look at the ExternalLoginCallback method. I've also pasted the version of the code as of 26 Oct 2020 below for future reference incase the repo goes away.
/// <summary>
/// Post processing of external authentication
/// </summary>
[HttpGet]
public async Task<IActionResult> ExternalLoginCallback()
{
// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
// lookup our user and external provider info
var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result);
if (user == null)
{
// this might be where you might initiate a custom workflow for user registration
// in this sample we don't show how that would be done, as our sample implementation
// simply auto-provisions new external user
user = await AutoProvisionUserAsync(provider, providerUserId, claims);
}
// this allows us to collect any additonal claims or properties
// for the specific prtotocols used and store them in the local auth cookie.
// this is typically used to store data needed for signout from those protocols.
var additionalLocalClaims = new List<Claim>();
additionalLocalClaims.AddRange(claims);
var localSignInProps = new AuthenticationProperties();
ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps);
ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps);
ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps);
// issue authentication cookie for user
// we must issue the cookie maually, and can't use the SignInManager because
// it doesn't expose an API to issue additional claims from the login workflow
var principal = await _signInManager.CreateUserPrincipalAsync(user);
additionalLocalClaims.AddRange(principal.Claims);
var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id;
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name));
// issue authentication cookie for user
var isuser = new IdentityServerUser(principal.GetSubjectId())
{
DisplayName = name,
IdentityProvider = provider,
AdditionalClaims = additionalLocalClaims
};
await HttpContext.SignInAsync(isuser, localSignInProps);
// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
// validate return URL and redirect back to authorization endpoint or a local page
var returnUrl = result.Properties.Items["returnUrl"];
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect("~/");
}

Firebase - Facebook Auth

I'm struggling to get my simple Facebook login to work. I'm simply trying to follow the example on the website but I run into this error:
Uncaught: LĀ {code: "auth/argument-error", message: "signInWithPopup failed: First argument "authProvider" must be a valid Auth provider."}
And here is what I'm trying to do:
export const createUserWithFacebook = () => {
let provider = firebase.auth.FacebookAuthProvider();
firebaseApp.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// firebaseApp.auth().signInWithRedirect(provider);
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
});
}
Can anyone point out what I'm doing wrong here? Thanks for your help!
At your firebase instance of the Facebook provider object should be:
let provider = new firebase.auth.FacebookAuthProvider();
this new will use firebase own constructor for the new instance.
instead of :
let provider = firebase.auth.FacebookAuthProvider();
here the link further details

SocketIO syncUpdate model for different user

I have been writing an web app, where individual user will post their task and will be displayed on their news feed. I have used socket io with angular fullstack framework by yeoman.
what I am trying to do is, when user adds new data to goal model, on socketSyncUpadtes, $rootScope.getUserGoals get updated and user see new entry. but I want that to happen for each user, thats why I included userid, to make sure, it only pulls user specific data.
But in reality what is happening is, all connected user getting this socket stream and getting their model updated too, but as page refreshed it is always only what that query pulls, only user specific data.
this is how I get user specific data
$rootScope.getUserGoals = function (userid){
//get current todo task views
$http.get('/api/goals/name/'+userid).success(function(goals) {
$rootScope.userGoalArr = goals;
socket.syncUpdates('goal', $rootScope.userGoalArr);
});
};
then it displays in view with this
<ul class = "holdTaskul" ng-repeat="view in filtered = userGoalArr | orderBy:'-created'">
I looked up on google and search a lot to figure out how to separate each user socket stream and attached them to their login credential. I have come across this socket-jwt and session token. I am not expert on socket. I would much appreciate if someone could point me to right direction about this, what I am intending to do..
my socket configuration is as follows for socket.syncUpdate function
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem);
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
I have also configured socket for sending auth token by this
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
query: 'token=' + Auth.getToken(),
path: '/socket.io-client'
});
and used socketio-jwt to send the secret session to socket service by that
module.exports = function (socketio) {
socketio.use(require('socketio-jwt').authorize({
secret: config.secrets.session,
handshake: true
}));

Resources