Toast Notification not triggering .NET with Angular - asp.net-core

Hope someone can help me clarify some doubts I have regarding popups and toasts. I have a .ASPNET Core application with users, I have a change password functionality and i'm trying to send a toastr.success message whenever the password is changed without an issue, how could I do this?
Currently, I have this method when the form is submited:
onSubmit() {
this.accountService.changePassword(this.updatePasswordForm.value).subscribe(response => {
}, error => {
this.validationErrors = error
})
From the component, the form value travels to the account service:
changePassword(model: any) {
this.router.navigateByUrl('/member/edit');
return this.http.post(this.baseUrl + 'account/changePassword', model);
And finally from here, to the controller:
[HttpPost("changePassword")]
public async Task<ActionResult<ChangePasswordDTO>> ChangePassword([FromBody] ChangePasswordDTO changePassword) {
//GET CURRENTLY LOGGED IN USER
var currentUserId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByIdAsync(currentUserId);
//CHANGE PASSWORD USING USERMANAGER
var result = await _userManager.ChangePasswordAsync(user, changePassword.OldPassword, changePassword.NewPassword);
if (!result.Succeeded) {
return Unauthorized("There was an error changing your password, please try again.");
} else return Ok("Password changed successfull");
When the controller fails to change the password, a toast pops up with the unauthorized message, but on success, nothing happens except the redirect and a console log with a 200 OK message. How and where could I add a success toast if the password is changed successfully?
Thank you for your help in advance!

Related

How to store user session after signup in remix-auth?

I'm using https://github.com/sergiodxa/remix-auth-github.
I was wondering, if I have a signup screen, what is the correct method to store the user session once the signup is finished, so the user does not have to log in again?
To explain this more clearly. Imagine I have a function:
async function signup(userInfo) {
await DB.insertUser(userInfo)
}
Once this is finished, I would like to store the user's session information inside of the cookie instead of having them log in again. What's the best method to do this?
Author here, if your Authenticator stores the data returned by signup then you could do something like this:
export let action: ActionFunction = async ({ request }) => {
// get the user info from the formData, however you are doing it, this
// depends on your app
let userInfo = await getUserInfo(request)
// register the user with your function
let user = await signup(userInfo)
// get the session object from the cookie header, the getSession should
// be the same returned by the sessionStorage you pass to Authenticator
let session = await getSession(request.headers.get("cookie"))
// store the user in the session using the sessionKey of the
// Authenticator, this will ensure the Authenticator isAuthenticated
// method will be able to access it
session.set(authenticator.sessionKey, user)
// redirect the user somewhere else, the important part is the session
// commit, you could also return a json response with this header
return redirect("/somewhere", {
headers: { "Set-Cookie": await commitSession(session) },
});
}
This way, now when you call authenticator.isAuthenticated(request) it will work and return the user object.

Vue + MSAL2.x + Azure B2C Profile Editing

First, I am not finding Vue specific examples using MSAL 2.x and we'd like to use the PKCE flow. I am having issues with the way the router guards are run before the AuthService handleResponse so I must be doing something wrong.
In my main.js I am doing this...
// Use the Auth services to secure the site
import AuthService from '#/services/AuthServices';
Vue.prototype.$auth = new AuthService()
And then in my AuthConfig.js I use this request to login:
loginRequest : {
scopes: [
"openid",
"profile",
process.env.VUE_APP_B2C_APISCOPE_READ,
process.env.VUE_APP_B2C_APISCOPE_WRITE
]
},
The docs say it should redirect to the requesting page but that is not happening. If user goes to the protected home page they are redirected to login. They login, everything is stored properly so they are actually logged in, but then they are sent back to the root redirect URL for the site, not the Home page.
When a user wants to login we just send them to the protected home page and there is a login method called in the router guard which looks like this:
router.beforeEach(async (to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const IsAuthenticated = await Vue.prototype.$auth.isAuthenticated()
console.log(`Page changing from ${from.name} to ${to.name}, requiresAuth = ${requiresAuth}, IsAuthenticated = ${IsAuthenticated}`)
if (requiresAuth && !IsAuthenticated)
{
next(false)
console.log('STARTING LOGIN')
Vue.prototype.$auth.login()
// Tried this
// Vue.prototype.$auth.login(to.path)
} else {
next()
}
})
In AuthServices.js I have this...
// The user wants to log in
async login(nextPg) {
// Tell B2C what app they want access to and their invitation ID if they are new
if (store.getters.userEmail != null) {
aCfg.loginRequest.loginHint = store.getters.userEmail
}
aCfg.loginRequest.state = "APP=" + store.getters.appCode
if (store.getters.appointmentLink != null && store.getters.appointmentLink != '') {
aCfg.loginRequest.state += ",ID=" + store.getters.appointmentLink
}
// Tried this
// if (nextPg && nextPg != '') {
// aCfg.loginRequest.redirectUrl = process.env.VUE_APP_B2C_REDIRECT_URL + nextPg
// }
return await this.msalInst.loginRedirect(aCfg.loginRequest)
}
I tried puting a nextPg parameter in the login method and adding a redirectUrl property to the login request but that gives me an error saying it is not one of the configured redirect URLs.
Also, I'm trying to make the user experience better when using the above technologies. When you look at the MSAL2.x SPA samples I see that when returning from a Profile Edit, a user is logged out and they are required to log in again. That sounds like a poor user experience to me. Sample here: https://github.com/Azure-Samples/ms-identity-b2c-javascript-spa/blob/main/App/authRedirect.js
Do I need to just create my own profile editing page and save data using MSGraph to prevent that? Sorry for the noob questions. Ideas?
Update - My workaround which seems cheesy is to add these two methods to my AuthService.js:
storeCurrentRoute(nextPath) {
if (!nextPath) {
localStorage[STOR_NEXT_PAGE] = router.history.current.path
} else {
localStorage[STOR_NEXT_PAGE] = nextPath
}
console.log('Storing Route:', localStorage[STOR_NEXT_PAGE])
}
reEstablishRoute() {
let pth = localStorage[STOR_NEXT_PAGE]
if (!!pth && router.history.current.path != pth) {
localStorage[STOR_NEXT_PAGE] = ''
console.log(`Current path is ${router.history.current.path} and reEstablishing route to ${pth}`)
router.push({ path: pth })
}
}
I call storeCurrentRoute() first thing in the login method and then in the handleResponse() I call reEstablishRoute() when its not returning from a profileEdit or password change. Seems like I should be able to make things work without this.
Update Number Two - When returning from B2C's ProfileEdit User Flow the MSAL component is not logging me out properly. Here is my code from my handlePolicyChange() method in my AuthService:
} else if (response.idTokenClaims[clmPolicy] === aCfg.b2cPolicies.names.editProfile) {
Vue.nextTick(() => {
console.log('BACK FROM Profile Change')
Vue.prototype.$swal(
"Success!",
"Your profile has been updated.<br />Please log in again.",
"success"
).then(async () => {
this.logout()
})
})
}
:
// The user wants to log out (all accounts)
async logout() {
// Removes all sessions, need to call AAD endpoint to do full logout
store.commit('updateUserClaims', null)
store.commit('updateUserEmail', null)
let accts = await this.msalInst.getAllAccounts()
for(let i=0; i<accts.length; i++) {
const logoutRequest = {
account: accts[i],
postLogoutRedirectUri: process.env.VUE_APP_B2C_REDIRECT_URL
};
await this.msalInst.logout(logoutRequest);
}
return
}
It is working fine until the call to logout() which runs without errors but I looked in my site storage (in Chrome's debug window > Application) and it looks like MSAL did not clear out its entries like it does on my normal logouts (which always succeed). Ideas?
As part of the MSAL auth request, send a state Parameter. Base64 encode where the user left off inside this parameter. MSAL exposes extraQueryParameters which you can put a dictionary object inside and send in the auth request, put your state Key value pair into extraQueryParameters.
The state param will be returned in the callback response, use it to send the user where you need to.

React native firebase - facebook sign-in not triggering onAuthStateChanged listener

this is the second problem Im having with RNFB facebook login.
Im following the official code sample provided by RNFB....code below
Problem is with the line firebase().signInWithCredential(facebookCredential);....its not triggering the firebase listener auth().onAuthStateChanged
All other code is running as it should and facebookCredential variable is populated correctly
import { firebase } from '#react-native-firebase/auth';
onFacebookButtonPress = async () => {
// Attempt login with permissions
const result = await LoginManager.logInWithPermissions([
'public_profile',
'email',
]);
if (result.isCancelled) {
throw 'User cancelled the login process';
}
// Once signed in, get the users AccesToken
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
throw 'Something went wrong obtaining access token';
}
// Create a Firebase credential with the AccessToken
//const facebookCredential = firebase.FacebookAuthProvider.credential(data.accessToken);
const facebookCredential = firebase.auth.FacebookAuthProvider.credential(
data.accessToken,
);
// Sign-in the user with the credential
firebase().signInWithCredential(facebookCredential);
};
I figured it out by putting a try/catch around the problematic line. Turned out was because I had already once signed in using Google.
An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address
You shouldn't wait till something goes wrong before you add try/ catch's. signInWithCredential returns many different types of errors, which you handle. From the docs:
firebase.auth().signInWithCredential(credential).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;
if (errorCode === 'auth/account-exists-with-different-credential') {
alert('Email already associated with another account.');
// Handle account linking here, if using.
} else {
console.error(error);
}
});
Please handle the other error cases too 🙂:
auth/account-exists-with-different-credential
auth/invalid-credential
auth/operation-not-allowed
auth/user-disabled
and more.

FeatherJS - Get user information with hook?

So im trying out FeatherJS and i was able to register a new user, request a token and also request protected data (using Authorization in the header).
Very important: I am using HTTP Rest API only. The docs seem to point often times to the client feathers module, which i don't use.
So currently i have a super simple setup, i have a message service with a text. One before hook to process the message. Here i want to get back the user information:
module.exports = function (options = {}) {
return async context => {
const text = context.data.text
const user = context.params.user;
context.data = {
text,
userId: user._id
}
return context;
};
};
this doesn't work. In my mdb i only get back:
{
"_id": "5c35ce18523501803f6a8d8d",
"text": "123",
"createdAt": "2019-01-09T10:34:00.774Z",
"updatedAt": "2019-01-09T10:34:00.774Z",
"__v": 0
}
i've tried to add the token, that i always submit when i post a message via Authorization, like so:
module.exports = function (options = {}) {
return async context => {
const text = context.data.text
const user = context.params.user;
const token = context.params.accessToken
context.data = {
text,
userId: user._id,
tokId: token
}
return context;
};
};
but it seems like i always just get the same result back like shown above.
Any ideas how i can get the user information back of the current user by using the accessToken?
Never used FeathersJS before, so just trying to understand the ecosystem and how to approach this in FeathersJS.
Any advice is appreciated! Thanks in advance everyone!
Not quite sure what exactly went wrong, but i got it now working by just creating a new project.
Now i did recreate this project actually before and got the issue as above , but this time it somehow worked.
For anyone who wants to know the steps i did to 'fix' it:
1.Create a new folder
2. feathers generate app
3. feathers generate authentication
4. feathers generate service (name for the service: messages)
5. feathers generate hook (name: process-msg, before hook, model: messages)
6. Add this to the hook process-msg:
module.exports = function (options = {}) {
return async context => {
const user = context.params.user;
const text = context.data.text;
context.data = {
userId: user.email,
text,
dateTime: new Date().getTime()
}
return context;
};
};
Use postman, register a new account then authenticate to get the token. Save token and add it as Authoriztation Header inside Postman. You should then get also back the user email from the user that is registered, simply because of the token that was added to the Authorization Header.
Greetings!
go to authentication.js and find app.service definition. Right in there, create an after hook and add the details you want the client to receive
app.service('authentication').hooks({
before: {
...//as you currently have it
},
after: {
create: {
hook => {
// hook.result.accessToken is already provided
delete hook.params.user.password
hook.result.user = hook.params.user;
hook.result.token_type = 'Bearer';
hook.result.exp = 3600;
}
}
}
})
I hope this helps
So, if I understand it correctly, you want to get the user object from the hook?
You can just use const user = context.user;to accomplish this.

Protractor not waiting for login redirect before continuing tests in AngularJS, any suggestion?

I have a standard username/password/submit button form, when the user clicks on the button the form submits with ng-submit="login.submit()" which does the login and on success redirects to the main page using ui.router ($state.go("main")).
The following test fails:
describe("login", function() {
beforeEach(function() {
var email = element(by.model("login.email"));
email.clear().sendKeys("mail");
var password = element(by.model("login.password"));
password.clear().sendKeys("pass");
var submit = element(by.id("submit"));
submit.click();
});
it("should be able to login", function() {
expect(element(by.css(".loginPage")).isPresent()).toBe(false);
expect(element(by.css(".mainPage")).isPresent()).toBe(true);
});
});
and if I try to add wait times around, I can see that the browser stays on the login page the whole time (after clicking on the button) - then I get a timeout.
After a successful login the browser receives a cookie with a token for authenticating each following request.
EDIT: with some tinkering I found out where it fails..
function login(email, pass) {
alert("it gets here");
return _auth.post({ username: email, password: pass }).then(function(data) {
alert("does not get here");
console.log("loginok, token:" +$browser.cookies().apiToken); //this should be the received token
return data;
});
}
EDIT2: the Auth service
var _auth = Restangular.withConfig(function(Configurer) {
Configurer.setBaseUrl("/");
}).service("auth/simple");
return {
login: login,
};
function login(email, pass) {
return _auth.post({ username: email, password: pass });
}
Manually everything works as expected.
#JoMendez's answer was very close but didn't work in my case. Used #DaveGray's here.
Had to wrap the isPresent() call in a function.
browser.wait(function() {
return element(by.css('.mainPage')).isPresent();
});
Try this:
it("should be able to login", function() {
browser.wait(element(by.css(".mainPage")).isPresent);//this is different from sleep, this will stop the excecution of all the protractor code that is after it, until the element is present, but it won't prevent the application of loading or if is redirecting, it will keep working.
expect(element(by.css(".loginPage")).isPresent()).toBe(false);
expect(element(by.css(".mainPage")).isPresent()).toBe(true);
});
});