I want to write a function loggedIn() in file auth.service.ts to check the token from local storage, and then verify it with firebase/php-jwt in server side. But the code in Typescript gives an infinite loop. Here is my code:
auth.service.ts
loggedIn(){
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
}
else {
const subs = this.http.post('http://localhost/url/to/myPHP.php', {"token":token})
.map(res=>res.json()).subscribe(data=>{
if(data.valid){
this.valid = true;
} else {
this.valid = false;
}
},
err=>console.log(err));
if (this.valid){
console.log("Valid");
return true;
} else {
console.log("Invalid");
return false;
}
}
}
Given token: valid token.
Result: give no error but infinite console.log of 'Valid' as well as return true, until the Apache down.
Given token: invalid token
Result: give no error but infinite console.log of 'Invalid' as well as return false, until the Apache down.
What I have tried:
loggedIn(){
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
}
else {
const subs = this.http.post('http://localhost/url/to/myPHP.php', {"token":token})
.map(res=>res.json()).subscribe(data=>{
if(data.valid){
this.valid = true;
} else {
this.valid = false;
}
},
err=>console.log(err));
if (this.valid){
console.log("Valid");
console.log(this.valid);
return true;
} else {
console.log("Invalid");
console.log(this.valid);
return false;
}
subs.unsubscribe();
return true;
}
}
The line subs.unsubscribe(); did stop the loop, yet it will literally unsubscribe the Observable<Response> and the code inside .subscribe() will not run. Please help.
Edit: Usage of loggedIn()
*ngIf="authService.loggedIn()
for 4 times in navbar component.
Inside auth.guard.ts
canActivate(){
if (this.authService.validToken){
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
In app.module.ts
{path:'profile', component:ProfileComponent, canActivate:[AuthGuard]}
I finally solve the problem. The problem with my code: .subscribe() is an async call (which actually scheduled for last/later execution). This is the situation:ngOnInit() : which located in component < ts > file
ngOnInit(){
this.authService.loggedIn();
console.log("10");
}
loggedIn() : which located in auth.service.ts file
loggedIn(){
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
}
else {
const subs = this.http.post('http://localhost/url/to/myPHP.php', {"token":token})
.map(res=>res.json()).subscribe(data=>{
if(data.valid){
console.log("1");
} else {
console.log("2");
}
console.log("3")
},
err=>console.log(err));
}
}
and then the result will be :
1013 which mean, anything you do inside the subscribe will change only after you need it (in many cases). So we need to do whatever we need to, inside the subscribe(), and fire it only when needed. In my case, I want to fire it if any changes apply to token inside local storage.
This is my solution
As I want it always check with the token. I used DoCheck()
Inside auth.service.ts
verifyToken(authToken) {
const body = {token:authToken};
return this.http.post('http://localhost/url/to/myPHP.php',body)
.map(res => res.json());
}
tokenExist(): boolean {
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
} else {
return true;
}
}
Inside navbar.component.ts
ngOnInit() {
this.curToken = localStorage.getItem('id_token');
this.loggedIn(this.curToken);
}
ngDoCheck(){
if (this.curToken != localStorage.getItem('id_token')){
this.curToken = localStorage.getItem('id_token');
this.loggedIn(this.curToken);
}
}
loggedIn(token){
if(this.authService.tokenExist()){
this.authService.verifyToken(token).subscribe(data=>{
if(data.valid){
this.validLogin = true;
} else {
console.log("Invalid Token");
this.validLogin = false;
}
});
} else {
console.log("Token Missing");
this.validLogin = false;
}
}
Of course, don't forget to implement DoCheck in the line
export class NavbarComponent implements OnInit, DoCheck
Related
I am building a sign in functionality using bloc pattern, if the entered credentials are invalid, bloc will return a authErrorState, so I will display a invalid credentials popup as soon as the bloc return a authError State
please check the code :
if (state is IsAuthLoadingState) {
return const LoadingSpinnerWidget();
} else if (state is IsAuthenticatedState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
stopTimer();
BlocProvider.of<AuthBloc>(context).add(LoadAuthStatus());
Navigator.pop(context, true);
});
} else if (state is AuthErrorState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
stopTimer();
showCustomPopUp(state.message);
});
}
Bloc code :
void _onLoginUser(LoginUser event, Emitter<AuthState> emit) async {
emit(IsAuthLoadingState());
final UserLoggedInResponse userDetails =
await authRepository.handleLoginUser(event.phoneNumber, event.otp);
if (userDetails.status == "success") {
for (var item in userDetails.wishlist) {
await _localRepo.addWishlistItem(item);
}
for (var item in userDetails.cart) {
await _localRepo.addCartItem(item);
}
for (var item in userDetails.recentSearches) {
await _localRepo.addRecentSearchTerm(item);
}
await _localRepo.addPurchasedItems(userDetails.purchasedItemIds);
await _localRepo.setIsAuthenticated(
userDetails.accessToken, userDetails.userId);
emit(IsAuthenticatedState());
} else {
emit(AuthErrorState(
message: userDetails.message, content: userDetails.content));
}
}
But, the invalid credentials popup written in authErrorState is getting called multiple times.
Any help is really appreciated. Thank you.
As I didn't found any alternative options, I someone tried to manage this for now like this,
I used a bool variable called isErrorShown, and it was set to false by default,
once the code in widgetsBinding is executed, it will set the isErrorShown to true, function is widgetsBinding checks the value of isErrorShown and executes only if it is false :
else if (state is AuthErrorState) {
print("error state");
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!isErrorShown) {
stopTimer();
if (state.message ==
"user does not exits, please create user") {
Navigator.pushReplacementNamed(context, '/create-user',
arguments: CreateUserPage(
showProfile: widget.showProfile,
phoneNumber: phoneNumberController.text,
otp: otpController.text,
));
// BlocProvider.of<AuthBloc>(context).add(LoadAuthStatus());
// Navigator.pushNamed(context, '/create-user');
} else {
showCustomPopUp(state.message);
}
isErrorShown = true;
}
});
Code to generate keys : Ps validPublicKey is a firebase code p256dh.
I donĀ“t know where is the problem. If is in the generate code, or the send notification.
I need to put it in php code yet.
navigator.serviceWorker.ready
.then(function(swreg) {
reg = swreg;
console.log(swreg.pushManager.getSubscription());
console.log(JSON.stringify(swreg.pushManager.getSubscription()));
return swreg.pushManager.getSubscription();
})
.then(function(sub) {
if (sub === null) {
console.log('criando a chave');
var validPublicKey = 'BIG2EEduGTIoAYMFC3zpq2lksUw-OLRUrq_abhLs1Y2Zbo_xDUGwlozyezbSKqNkYylNN2yWKV5adB0819nQ1y0';
var convertValidPublicKey = urlBase64ToUint8Array(validPublicKey);
return reg.pushManager.subscribe({
userVisibleOnly:true,
applicationServerKey:convertValidPublicKey
});
} else {
//we have
}
}).then(function(newSub) {
return fetch('https://???????.firebaseio.com/subscriptions.json', {
method:'POST',
headers: {
'Content-Type':'application/json',
'Accept':'application/json'
},
body:JSON.stringify(newSub)
})
}).then(function(res) {
if (res.ok) {
displayConfirmNotification();
}
}).catch(function(err) {
console.log(err);
});
}
I wonder if it is good enough to test if the reference exists
BEFORE I start a transaction on this reference?
e.g: by using .once('value') and snapshot.exists()
I mean if the check is outside the transaction isn't there a risk another user to delete the reference just after the check and before the transacton executor function?
==== edited to include the minimal complete code =====
here is my data in realtime database:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 2
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a" ---- offering user
And here is my code flow:
===== index.js =====
entries = require('./entries');
/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
console.log('data: ' + JSON.stringify(data));
return entries.askSeats(data);
});
here is my test data sent by Postman:
{
"data":
{
"uid": "-FGKKSDFGK12387sddd", ---- the requesting/asking user
"id": "-LKpCACQlL25XTWJ0OV_",
"details":
{
"direction": "city",
"seatsCount": 1,
"timeToGo": 5
}
}
}
===== entries.js =======
exports.askSeats = function(data) {
const TAG = '[askSeats]: ';
var entryRef = db.ref('activeOffers/' + data.id);
return globals.exists(entryRef)
.then((found)=>{
if (found) {
return dealSeats(entryRef, data);
} else {
return 'Offer not found [' + data.id + ']';
}
});
}
===== globals.js ======
exports.exists = (ref)=>{
return ref.once('value')
.then((snapshot)=>{
return (snapshot.exists());
});
}
===== entries.js =====
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
throw new Error('You've already made a deal.');
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
} else {
throw new Error('Not enought seats.');
}
}
return entry;
})
.then((success)=>{
return success.snapshot.val();
})
.catch((error)=>{
return Promise.reject(error);
});
}
Btw: is this 'throw new Error(......)' is the correct way to break the transaction ?
========= updated with final source ===
Thanks to Doug Stevenson.
So here is my final source that is working fine. If someone sees a potential problem please let me know. Thanks.
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
var abortReason;
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
abortReason = 'You already made a reservation';
return; // abort transaction
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
// Reservation is made
} else {
abortReason = 'Not enought seats';
return; // abort transaction
}
}
return entry;
})
.then((result)=>{ // resolved
if (!result.committed) { // aborted
return abortReason;
} else {
let value = result.snapshot.val();
if (value) {
return value;
} else {
return 'Offer does not exists';
}
}
})
.catch((reason)=>{ // rejected
return Promise.reject(reason);
});
}
If you read a value before a transaction, then read it again inside the transaction, you have absolutely no guarantee that the second read inside the transaction will yield the same result as the initial read outside before the transaction. It could be modified by the time the transaction is performed.
If you want a truly atomic update, only check value that participate in the transaction within the transaction itself, and make a decision about what to do in the transaction handler.
I am using keystone#0.2.32. I would like to change the post category to a tree structure. The below code is running well except when I create a category, it goes into a deadlock:
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* PostCategory Model
* ==================
*/
var PostCategory = new keystone.List('PostCategory', {
autokey: { from: 'name', path: 'key', unique: true }
});
PostCategory.add({
name: { type: String, required: true },
parent: { type: Types.Relationship, ref: 'PostCategory' },
parentTree: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
PostCategory.relationship({ ref: 'Post', path: 'categories' });
PostCategory.scanTree = function(item, obj, done) {
if(item.parent){
PostCategory.model.find().where('_id', item.parent).exec(function(err, cats) {
if(cats.length){
obj.parentTree.push(cats[0]);
PostCategory.scanTree(cats[0], obj, done);
}
});
}else{
done();
}
}
PostCategory.schema.pre('save', true, function (next, done) { //Parallel middleware, waiting done to be call
if (this.isModified('parent')) {
this.parentTree = [];
if(this.parent != null){
this.parentTree.push(this.parent);
PostCategory.scanTree(this, this, done);
}else
process.nextTick(done);
}else
process.nextTick(done); //here is deadlock.
next();
});
PostCategory.defaultColumns = 'name, parentTree';
PostCategory.register();
Thanks so much.
As I explained on the issue you logged on Keystone here: https://github.com/keystonejs/keystone/issues/759
This appears to be a reproducible bug in mongoose that prevents middleware from resolving when:
Parallel middleware runs that executes a query, followed by
Serial middleware runs that executes a query
Changing Keystone's autokey middleware to run in parallel mode may cause bugs in other use cases, so cannot be done. The answer is to implement your parentTree middleware in serial mode instead of parallel mode.
Also, some other things I noticed:
There is a bug in your middleware, where the first parent is added to the array twice.
The scanTree method would be better implemented as a method on the schama
You can use the findById method for a simpler parent query
The schema method looks like this:
PostCategory.schema.methods.addParents = function(target, done) {
if (this.parent) {
PostCategory.model.findById(this.parent, function(err, parent) {
if (parent) {
target.parentTree.push(parent.id);
parent.addParents(target, done);
}
});
} else {
done();
}
}
And the fixed middleware looks like this:
PostCategory.schema.pre('save', function(done) {
if (this.isModified('parent')) {
this.parentTree = [];
if (this.parent != null) {
PostCategory.scanTree(this, this, done);
} else {
process.nextTick(done);
}
} else {
process.nextTick(done);
}
});
I think it's a bug of keystone.js. I have changed schemaPlugins.js 104 line
from
this.schema.pre('save', function(next) {
to
this.schema.pre('save', true, function(next, done) {
and change from line 124 to the following,
// if has a value and is unmodified or fixed, don't update it
if ((!modified || autokey.fixed) && this.get(autokey.path)) {
process.nextTick(done);
return next();
}
var newKey = utils.slug(values.join(' ')) || this.id;
if (autokey.unique) {
r = getUniqueKey(this, newKey, done);
next();
return r;
} else {
this.set(autokey.path, newKey);
process.nextTick(done);
return next();
}
It works.
I'm having a problem with the following situation.
I have an ascx which contains a submit button for a search criteria and I am trying to call a validation function in a js file I've used throughout the site (this is the first time I'm using it in an ascx).
Now I've just tried this:
<script type="text/javascript" src="js/jquery-1.3.2.js"></script>
<script type="text/javascript" src="js/jsAdmin_Generic_SystemValidation.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$(".submitBtn").click(function (e) {
alert("test");
alert($.Validate());
alert("test 2");
});
});
</script>
The file is being referenced correctly as I am already seeing posts in Firebug that are done by it.
This is the function:
jQuery.extend({
Validate: function () {
var requiredElements = $('.required').length; // Get the number of elements with class required
$('.required').each(function () {
// If value of textbox is empty and have not
// yet been validated then validate all required
// elements. i.e.
if (($(this).val() == "") || ($(this).hasClass("validationError")) || ($(this).hasClass("validationAlert")) || ($(this).hasClass("validationOk") == false)) {
validate($(this));
}
});
if ($('.validationOk').length == requiredElements) {
return true;
} else {
return false;
}
},
// Another extended function, this function
// is used for pages with the edit-in-place
// feature implemented.
validateElement: function (obj) {
var elementId = obj.attr("id"); // id of the button clicked.
var flag = 0;
if (elementId.toLowerCase() == "paymentmethodid") {
// Case elementId = paymentMethodId then check all the
// elements with css class starting with openStorage
var requiredElements = $(document).find("input[class*='openStorage']").length; // Get the number of elements with css class starting with openStorage
// Loop through all the elements with css class containing
// openStorage abd validate each element.
$(document).find("input[class*='openStorage']").each(function () {
if (($(this).val() == "") || ($(this).hasClass("validationError")) || ($(this).hasClass("validationAlert"))) {
validate($(this));
}
if ($(this).hasClass("validationOk")) {
flag++;
} else if (($(this).hasClass("validationError")) || ($(this).hasClass("validationAlert"))) {
flag--;
}
});
// If all elements are valid return true else return false
if (flag == requiredElements) {
return true;
} else {
return false;
}
} else if (elementId.toLowerCase() == "registeredfortax") {
if (($('.TaxRegistrationNumber').val() == "") || ($('.TaxRegistrationNumber').hasClass("validationError")) || ($('.TaxRegistrationNumber').hasClass("validationAlert"))) {
validate($('.TaxRegistrationNumber'));
}
if ($('.TaxRegistrationNumber').hasClass("validationOk")) {
return true;
} else {
return false;
}
} else {
var elementClass = "." + elementId;
if (($(elementClass).val() == "") || ($(elementClass).hasClass("validationError")) || ($(elementClass).hasClass("validationAlert")) || ($(elementClass).hasClass("validationOk") == false)) {
validate($(elementClass));
}
if ($(elementClass).hasClass("validationOk") && ($(elementClass).hasClass("required"))) {
return true;
} else if ($(elementClass).hasClass("required") == false) {
return true;
}else {
return false;
}
}
}
});
Now at first I was getting "Validate() is not a function" in firebug. Since I did that alert testing, I am getting the first alert, then nothing with no errors.
Can anyone shed some light?
Thanks
Are you using the extend method properly? ...
http://api.jquery.com/jQuery.extend/