-
Notifications
You must be signed in to change notification settings - Fork 14
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
Conversation
src/model.ts
Outdated
} | ||
|
||
static set credentialStorageBackend(backend: StorageBackend | undefined) { | ||
if (this.baseClass === JSORMBase) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this 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.
src/credential-storage.ts
Outdated
return null | ||
} | ||
getJWT(): string | undefined { | ||
return this._backend.getItem(this._jwtKey) || undefined |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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") { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
test/unit/model.test.ts
Outdated
@@ -438,7 +437,8 @@ describe("Model", () => { | |||
describe("class options", () => { | |||
const config = { | |||
apiNamespace: "api/v1", | |||
jwt: "abc123" | |||
jwt: "abc123", | |||
jwtStorage: 'foobarJWT', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trailing comma
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.
There was a problem hiding this 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') { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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.