I'm currently integrating Amplify/Cognito into a server side rendered Angular 5 application. Obviously the server side rendered application has no access to local storage so I've configured Ampify to use cookie storage instead
Auth: {
...
cookieStorage: {
domain: 'localhost',
expires: 365,
path: '/',
secure: false
},
...
}
But when the server rendered application is loaded it shows these two errors when it tries to retrieve the cookies
[WARN] 51:02.95 Cache - getItem failed! TypeError: Cannot read property 'getItem' of null
[WARN] 51:02.140 Cache - getItem failed! TypeError: Cannot read property 'getItem' of null
I'll hazard a guess and say that's because it's using document.cookie
and that doesn't exist in the node environment. Are there any suggestions on how I can get the server rendered application to read the cookies from Amplify?
for node it has an in memory storage, see https://github.com/aws/aws-amplify/tree/master/packages/amazon-cognito-identity-js/lib
Forgive me if this comes across as a bit of a stupid question but how would you tell Amplify to use memory storage when you configure it?
Looking at the code, if you pass in cookieStorage
in your config you then set userPoolData.Storage
but I can't see how else you can pass any other storage via Amplify.configure
I'm getting the same error while running unit tests for components that include aws-amplify
.
Looking at Amplify.configure
options I can't find where such memory storage would go as a setting.
Don't have full SSR yet, but I was able to check if there user is authenticated by looking at the cookie for CognitoIdentityServiceProvider.${userPoolWebClientId}.LastAuthUser
and then the JWT which is also in the cookie.
Can anyone explain if it's even possible to use Amplify server-side?
I presume you'd require the module...
const amplify = require('aws-amplify');
I then tried the following...
const Amplify = amplify.default;
Amplify.configure({
Auth: {
identityPoolId: '',
region: '',
userPoolId: '',
userPoolWebClientId: '',
}
});
Amplify.Auth.signIn('foo', 'bar')
.then(data => console.log(data))
.catch(err => console.log(err));
This ^^ errors when run via node.
I think Amplify ultimately wouldn't work because it uses client-side APIs like fetch
. I've tried installing packages to support fetch in node (just as an example) but I think it's too dependent on client-side implementations 馃
Note: I don't know JS (or Node) that well at all - so there might be other ways.
@Integralist I think it can be used on server-side, we were using it to handle user signin/signup with it. the only thing we need to do is add this on top of the entry js file
import fetch from 'node-fetch';
global.fetch = global.fetch || fetch;
It will warn you about getItem, but that's fine.
@crysislinux that didn't work for me.
I get the error...
import fetch from 'node-fetch';
^^^^^^
SyntaxError: Unexpected token import
at new Script (vm.js:51:7)
at createScript (vm.js:136:10)
at Object.runInThisContext (vm.js:197:10)
at Module._compile (internal/modules/cjs/loader.js:618:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:665:10)
at Module.load (internal/modules/cjs/loader.js:566:32)
at tryModuleLoad (internal/modules/cjs/loader.js:506:12)
at Function.Module._load (internal/modules/cjs/loader.js:498:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:695:10)
at startup (internal/bootstrap/node.js:201:19)
[nodemon] app crashed - waiting for file changes before starting...
@Integralist That's because you don't have ES6 transpiling it seems.
A quick google search would have gotten you the answer:
https://stackoverflow.com/questions/39436322/node-js-syntaxerror-unexpected-token-import
Just replace
import fetch from 'node-fetch';
with
const fetch = require('node-fetch');
This issue just became a holder for other questions rather than the one asked by OP (@mogusbi).
I'm still not sure how to use Memory Storage...
Nah, it's looking for localStorage.
If you grep the aws-amplify
code in node_modules
, you'll see this:
function BrowserStorageCache(config) {
var _this = this;
var cacheConfig = config ? Object.assign({}, Utils_1.defaultConfig, config) : Utils_1.defaultConfig;
_this = _super.call(this, cacheConfig) || this;
_this.config.storage = cacheConfig.storage;
logger.debug('Using AsyncStorageCache');
return _this;
}
(Looks like it was transpiled to ES5 from TypeScript)
So it can take a localStorage polyfill in the config as storage
.
You could use a polyfill from npm, or just use the ghetto polyfill from the AWS App Sync docs:
https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-javascript.html
Like this:
const storage = {
store: {},
getItem(key) {
return this.store[key]
},
setItem(key, value) {
this.store[key] = value
},
removeItem(key) {
delete this.store[key]
}
}
config = { storage, ...config }
We've added a polyfill for this that uses the InMemoryStorage
in place that should address this:
https://github.com/aws/aws-amplify/blob/master/packages/aws-amplify/src/Common/Polyfills/Polyfills.ts
Please let us know if you still have issues here.
Was this pushed? Seems like this is still happening. It shows a warn like this four times, then hangs. The debug reveals that 'AWSPinpointProvider' is trying to connect, but (since I didn't configure it) it fails. Could be a red herring.
[WARN] 49:28.401 Cache - getItem failed! TypeError: Cannot read property 'getItem' of null
Should I create a new issue for this?
Yup, still getting the same issue:
WARN] 26:47.552 Cache - getItem failed! TypeError: Cannot read property 'getItem' of null
And page hangs.
Has this been resolved please? Getting the same error
@jwulf is the code you pasted meant to replace the one in BrowserStorageCache?
No mate, that's the enriched configuration object that you pass into the constructor to inject a polyfill implementation.
I haven't looked at this in a while, but quickly it looks like you pass that to Amplify.configure in https://github.com/aws/aws-amplify/issues/493#issuecomment-385999416
Thanks @jwulf. Will try that out.
For achieving SSR this is what I've done, just in case it helps someone. I'm using Koa but I think it should be easy to modify it for Express or something else.
In the server.js file:
import Amplify from 'aws-amplify';
import fetch from 'node-fetch';
...
// Middleware for adding storage polyfill that uses incoming cookies
app.use(async (ctx, next) => {
Amplify.configure({
Auth: {
...
},
Storage: {
// This one (uppercase) is for S3
},
storage: {
store: {},
getItem(key) {
return ctx.cookies.get(key);
},
setItem(key, value) {
return ctx.cookies.set(key, value, { httpOnly: false });
},
removeItem(key) {
ctx.cookies.set(key, '');
},
},
API: {
...
},
});
await next();
});
Then in the client, in the app component:
import Amplify from 'aws-amplify';
...
if (isClient) {
Amplify.configure({
Auth: {
...
cookieStorage: {
domain: ...,
expires: ...,
path: ...,
secure: ...,
},
},
Storage: {
...
},
API: {
...
},
});
}
@juanjcampos thanks for sharing your solution. Can you please show the complete solution? I, and I think many other people, will appreciate your help.
Most helpful comment
Nah, it's looking for localStorage.
If you grep the
aws-amplify
code innode_modules
, you'll see this:(Looks like it was transpiled to ES5 from TypeScript)
So it can take a localStorage polyfill in the config as
storage
.You could use a polyfill from npm, or just use the ghetto polyfill from the AWS App Sync docs:
https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-javascript.html
Like this: