Skip to content

Always delegate JWT to storage backend #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 21, 2018

Conversation

wadetandy
Copy link
Contributor

Previously we had an incomplete implementation of the
CredentialStorageBackend concept, as we were initializing from and
storing to it, but we also memoized it into the model class itself,
which meant that particularly when using localStorage, an update from
outside of JSORM wouldn't track into already initialized JSORM models.
Now it's possible to pass an InMemoryStorageBackend instead of the
default localStorage, which will isolate a particular model and prevent
it from persisting.

@wadetandy
Copy link
Contributor Author

@richmolj

src/model.ts Outdated
}

static set credentialStorageBackend(backend: StorageBackend | undefined) {
if (this.baseClass === JSORMBase) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am only choosing to do this because it was the way to preserve as much of the previous behavior as possible while allowing us to fix this functionality. If we are fine with users being able to set a base credential that must be manually overridden at lower levels (instead of setting one for each base class by default), then I'm fine with that as well, it will just result in a couple more breaking changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with it. I see manually overriding at lower-levels as a non-starter. I would like to somehow share this between here and 186-188

Copy link
Contributor

@richmolj richmolj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments but looks good overall. Suggest applying this to Nimitz first to verify everything works as expected.

return null
}
getJWT(): string | undefined {
return this._backend.getItem(this._jwtKey) || undefined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getItem specifically works to return null instead of undefined, but then we just convert it back to undefined?

src/model.ts Outdated
ModelClass[k] = config[k]
}
}

// JWT depends on some other settings, so wait for everything
// else before initializing it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this a bit more? This and line 121 jumped out to me. I would think we'd just call the setter as normal, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per my comment above, a lot of weirdness here is around ensuring backwards compatibility, which we can always choose to not do. Essentially if we don't do this, the order of declaration of jwtStorage and jwt will change where the provided jwt gets persisted to. My ideal world would have us get rid of the jwt API entirely and just use setJWT and getJWT

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My ideal world would have us get rid of the jwt API entirely and just use setJWT and getJWT

I'm fine with this if we have to. But I'm not 100% clear on why calling setJWT underneath the hood of jwt= wouldn't work.

src/model.ts Outdated
}

static set credentialStorageBackend(backend: StorageBackend | undefined) {
if (this.baseClass === JSORMBase) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with it. I see manually overriding at lower-levels as a non-starter. I would like to somehow share this between here and 186-188

src/model.ts Outdated
throw new Error(`Cannot access credentials on JSORMBase`)
}

if (!this._credentialStorageBackend) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could call the non _ version and get lines 186-188 for free?

src/model.ts Outdated
static set jwt(token: string | undefined) {
// If setting the JWT on a model class directly, we want it to have its own
// credential storage
this._initializeCredentialStorage()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to double-check this isn't having an unexpected side-effect. If Post and Comment subclass ApplicationRecord, and Comment.first() fires and gets an updated JWT in the headers of the response, are we going to hit this method and inadvertently store the updated JWT on Comment instead of ApplicationRecord?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, as the internal parts of the library should all be using setJWT which won't have this behavior.

src/model.ts Outdated
}

if (!this._credentialStorageBackend) {
if (this.jwtStorage && typeof localStorage !== "undefined") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting that this.jwtStorage could be false and we'd still create an in-memory backend. I don't care too much but heads up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that's the intended functionality. Essentially In-memory store is the same as not storing it.

@@ -235,20 +230,35 @@ describe("authorization headers", () => {

await Author.find(1)
})

describe("when JWT is update from outside the model classes", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@@ -438,7 +437,8 @@ describe("Model", () => {
describe("class options", () => {
const config = {
apiNamespace: "api/v1",
jwt: "abc123"
jwt: "abc123",
jwtStorage: 'foobarJWT',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing comma

@wadetandy
Copy link
Contributor Author

cc @AndrewO

Previously we had an incomplete implementation of the
CredentialStorageBackend concept, as we were initializing from and
storing to it, but we also memoized it into the model class itself,
which meant that particularly when using localStorage, an update from
outside of JSORM wouldn't track into already initialized JSORM models.
Now it's possible to pass an InMemoryStorageBackend instead of the
default localStorage, which will isolate a particular model and prevent
it from persisting.
Copy link
Contributor

@richmolj richmolj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one comment that's up to you, otherwise LGTM 👍 !

src/model.ts Outdated
@@ -118,11 +118,18 @@ export const applyModelConfig = <T extends typeof JSORMBase>(
let k: keyof ModelConfigurationOptions

for (k in config) {
if (config.hasOwnProperty(k)) {
if (config.hasOwnProperty(k) && k !== 'jwt') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm fine with this with a comment. Alternatively, I wonder if it might make more sense to manually do the JWT setup on line 119, then end the function with iterating props and assigning them. This way the logic isn't broken up between line 121 and 130, might read a bit more sensibly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do the JWT setup on line 119

Do you mean dealing specifically with all of the jwt-related ones and then finishing up with everything else in the config?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

this._credentialStorageBackend
)
static set jwtStorage(val : string | false) {
if (val !== this._jwtStorage) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the downside if this check were removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using an in-memory store and running ApplicationRecord.jwtStorage = ApplicationRecord.jwtStorage would wipe out the record. Otherwise nothing I can think of.

@wadetandy
Copy link
Contributor Author

@richmolj fix pushed

@wadetandy wadetandy merged commit 0e38b6d into jsonapi-suite:master Mar 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants