Does it matter where Apollo local resolvers are? - apollo

Let's say I fetch data with something like:
mutation Signup ($input: SignupInput!) {
signup (input: $input) {
success
message
login #client
}
}
and on the server
signup (input: SignupInput!): Response!
The docs say:
When Apollo Client executes this query and tries to find a result for the [login] field, it runs through the following steps:
Has a resolver function been set that is associated with the field name [login]? If yes, run and return the result from the resolver function.
(ref)
Based on that, it shouldn't matter where the login resolver is attached. However, it does matter (it executes the resolver only if it's a property of Response...).
So what is the intended behavior, the behavior or the documentation?
Thanks, just trying to understand this 😇

Related

AWS Amplify Auth.updateUserAttributes() clear localStorage/sessionStorage

I am writing a web-based app that uses AWS Cognito as the authentication service. I use 'aws-amplify' to implement the client-app.
I am using Auth.updateUserAttributes() to update a custom attribute of users on Cognito. However, I found that the call of this function would clear all the Cognito-related items, including idToken, refreshToken, and accessToken stored in localStorage. As a result, the web app behaves like sign-out.
Here is the code about Auth's configuration
Amplify.configure({
Auth: {
userPoolId: process.env.REACT_APP_AWS_COGNITO_USER_POOL_ID,
region: process.env.REACT_APP_AWS_COGNITO_REGION,
userPoolWebClientId: process.env.REACT_APP_AWS_COGNITO_APP_CLIENT_ID,
storage: window.localStorage,
authenticationFlowType: 'CUSTOM_AUTH',
},
});
and the code I wrote to update the user's attribute. (I followed the example code from the amplify docs https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/#managing-user-attributes)
let user = await Auth.currentAuthenticatedUser();
console.log(JSON.stringify(localStorage)); // it outputs the localstorage with idToken,
// refreshToken, accessToken and other items
// start with 'CognitoIdentityServiceProvider'
const result = await Auth.updateUserAttributes(user, {
'custom:attributes_1': '123456789',
});
console.log(result); // OUTPUT: SUCCESS
console.log(JSON.stringify(localStorage)); // Only 'amplify-signin-with-hostedUI'.
// idToken, refreshToken, accessToken and
// other items were gone. No key, no value.
After the last line, I could not interact with the web page anymore. If I refreshed the web page, I found I had signed out and had to sign in again.
It was still the same if I changed the storage for Auth from localStorage to sessionStorage.
Here are my questions:
Is this kind of behavior normal? Does Auth.updateUserAttributes() leads to a force sign-out?
If it's true, is there any way to avoid a mandatory sign-out?
If it's not, what's wrong with my code or configuration? Or should I do some specific configuration for the Cognito service?
Thanks a lot!
Well, I figured it out after reading the source code of aws-amplify.
Behind the call of Auth.userUpdateAttributes, amplify will finally call CognitoUser.refreshSession(refreshToken, callback, clientMetadata) (https://github.com/aws-amplify/amplify-js/blob/f28918b1ca1111f98c231c8ed6bccace9ad9e607/packages/amazon-cognito-identity-js/src/CognitoUser.js#L1446). Inside this function, amplify sends an 'InitiateAuth' request to Coginito. If an error of 'NotAuthorizedException' happens, amplify calls clearCachedUser() that delete everything I mentioned in my question from the localStorage.
There was an error of 'NotAuthorizedException' happening and reported by the network work monitor of Chrome Browser'. I thought it was generated after the sign-out-like behavior. However, it turned out to be triggered because no deviceKey was passed to the request's parameters.
So the whole story was:
I set remember device options in Cognito;
I used 'CUSTOM_AUTH' as the authentication flow type;
When a user successfully signed in to my application, Cognito didn't give the client the deviceKey because of the 'CUSTOM_AUTH' authentication flow type.
When Auth.userUpdateAttributes() was called, CognitoUser.refreshSession() was called behind it. It attached no deviceKey to Cognito when it sent ana request to asked Cognito to refresh the token. The Cognito rejected the request with an error of 'NotAuthorizedException'. The CognitoUser.refreshSession() handled the error and called clearCachedUser() to delete the stored tokens and other info from the localStorage.
My final solution is to turn off the remember device option since I have to use 'CUSTOM_AUTH' as the authentication flow type according to my application's functional requirements.
According to https://aws.amazon.com/premiumsupport/knowledge-center/cognito-user-pool-remembered-devices/, the remember device function only works when the authentication flow type is set as 'USER_SRP_AUTH'.

AWS Cognito + aws-amplify: session state always keep user logged in?

I'm using AWS Cognito and aws-amplify to manage user authentication. When I load up my app, I call Auth.currentSession() which seems to always return the user I was logged in as if I do not explicitly log out by calling Auth.signOut().
I'm fine with this should the user choose a "keep user logged in", but if they don't, how would I go about making sure the user gets logged out once they leave the app?
I tried adding an event listener in my login() method but that didn't work i.e. the user was still logged in when I returned to the app:
.
.
.
if (!keepSignedIn) {
window.addEventListener('unload', function(event) {
Auth.signOut();
});
}
I'm pretty sure the logout() method creates a promise - it operates asynchronously. So the page is probably being destroyed before the promise's logout code is executed.
You can confirm this by executing console.log(Auth.signOut());. If it's a promise it'll log Promise { <pending> }
There's no way to halt unloading of the page, as that would be bad if we could.
What you need is a synchronous signout function. Fortunately, you can just clear the browser local storage, which is a synchronous operation. (Local storage is where Amplify stores the auth tokens.)
if (!keepSignedIn) {
window.addEventListener('unload', function(event) {
localStorage.clear();
});
}
Depending on your situation you may need to instead find and remove individual local storage items, instead of clearing them all.
You can clear Amplify cache before Auth.signOut()
import AmplifyCache from '#aws-amplify/cache';
AmplifyCache.clear();

loopback angular sdk login error

I'm trying to log in a new user, but i have some problems.
I've created admin role, admin user, RoleMapping and the Principal. Then i've created the client services using the command below:
lb-ng ../server/server.js js/lb-services.js
When i try to login using the command
User.login({"email":"email","password":"password"})
I receive:
POST http://localhost:3000/users/login?include=user 404 (Not Found)
What is the query string parameter ?include=user?
I've tried to login with PostMan and everything works correctly.
What I'm doing wrong?
Thanks in advance.
Good afternoon, it seems to be a little bit old post but I had this same problem and found a solution about it.
If we check the file generated by lb-ng for login function it states:
Login a user with username/email and password.
*
* #param {Object=} parameters Request parameters.
*
* - include – {string=} - Related objects to include in the response. See the description of return value for more details.
* Default value: user.
And if we check return value's description:
* The response body contains properties of the AccessToken >created on login.
* Depending on the value of `include` parameter, the body may contain additional properties:
*
* - `user` - `{User}` - Data of the currently logged in user. (`include=user`)
So include=user seems to add to response a property with user logged in data in a property called "user" in this case, but you can change the property's name for whatever you want.
In my case the solution was following Robins answer, and change the default server. It seems that lb-ng solution create code to look API calls into the server where is running the client. If you use another server you must change app.config this way, stated in loopback docs:
angular.module('my-app-module')
.config(function(LoopBackResourceProvider) {
// Use a custom auth header instead of the default 'Authorization'
LoopBackResourceProvider.setAuthHeader('X-Access-Token');
// Change the URL where to access the LoopBack REST API server
LoopBackResourceProvider.setUrlBase('http://api.example.com/');
});
Let the header line if you want that change, but it's mandatory to use the second line changing 'http://api.example.com' with the server you use to serve the REST API.
(Hope it helps to anyone with this problem).

What's a good Login Auth flow

How would I implement a solution like described below? Would it be easier to jack in something like ember-simple-auth? All examples I find implement their own login form, that's not something I'll have. My login will live on its own domain.
Scenario:
user visits https://myapp.com.
In some initializer I see that they don't have an/don't have a valid access token so I redirect them to https://login.myapp.com?redirect_uri=https://myapp.com
login.myapp.com sign the user in hand hands them an access token and redirects user to https://myapp.com?access_token={token}
Myapp boots again only this time it finds a valid access token (from the url) and sets some variable that the user is logged in. And then adds the token as header bearer onto all future xhr requests.
Is this a logical login approach? Some fundamental flaws I'm missing? Do you know any tutorials doing something like this? Thanks!
That flow would be pretty easy to implement with Ember Simple Auth. You simply need to define a custom authenticator that you authenticate the session with when the application starts up again after the redirect. The authenticator's authenticate method would look sth. like this:
authenticate: function(options) {
return new Ember.RSVP.Promise(function(resolve, reject) {
resolve({ token: /* the token you get from the query string */ });
});
}
Authenticating the session with the authenticator in an initializer then is as easy as:
this.get('session').authenticate('your-authenticator');

Ember Simple Auth different redirects after authentication

I'm using Ember simple auth in my app and it's working great, but I've run into a scenario that I'm having trouble getting around.
The library lets you specify the route to redirect to after a successful authentication by overriding routeAfterAuthentication: 'index'. This is working fine, however, I'm finding myself in a situation where I want to have two different types of redirects. When a user first logs in, I want them to go to /dashboard, but when they first sign up and authenticate, I want them to go /settings.
I was hoping to be able to do something like this after successfully creating an account, but it's still trying to use the routeAfterAuthentication option for the transition:
var _this = this;
this.set('identification', _this.get('email'));
this.set('password', password);
this.send('authenticate', function() {
_this.transitionToRoute('settings');
}, function() {});
Is there a way to specify which route to transition to after authenticating on one-off basis? Maybe there's a better way to log someone after they create an account without needing to go through the authenticate() method?
You can simply override the sessionAuthenticated method in the application route and implement your own logic. Beware though that the default implementation will not always transition to routeAfterAuthentication-- if there's a previously intercepted transition stored in the session, sessionAuthenticated will retry that instead.