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
[Feature] Identity verification implementation #2030
base: main
Are you sure you want to change the base?
Conversation
268d1a7
to
97e4b8a
Compare
81ebe0b
to
ee6ff69
Compare
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.
It's not testable yet, but have you thought about how to handle the anonymous user that would have been automatically created in initWithContext
since I didn't see any changes for anonymous users, and unsubscribing the user on logout
(if JWT-enabled)?
@@ -359,12 +363,23 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { | |||
currentIdentityOneSignalId = identityModelStore!!.model.onesignalId | |||
|
|||
if (currentIdentityExternalId == externalId) { | |||
// login is for same user that is already logged in, fetch (refresh) | |||
// the current user. | |||
identityModelStore!!.model.jwtToken = jwtBearerToken |
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.
main
has changed a bit since some PRs have been merged; this refresh on login was removed in a previous PR
} | ||
} | ||
|
||
override fun updateUserJwt(externalId: String, token: String) { | ||
if (!identityModelStore!!.model.externalId.equals(externalId)) |
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.
This returns early but the SDK may need to update JWT for previous users?
* | ||
*/ | ||
class UserJwtInvalidatedEvent( | ||
val external_id: String |
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.
Remove underscore, rename to externalId
.
This was a mistake in the spec, and just corrected it there.
cb7a790
to
3238d9a
Compare
22fe20f
to
4d1a0b4
Compare
) | ||
) { | ||
fun invalidateJwt() { | ||
model.setStringProperty( |
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.
This will add jwt_token
as an alias on the Identity Model and get enqueued as a set-alias operation. If you look at IdentityModelStoreListener
, any changes are detected as aliases.
var jwt: String? | ||
get() = getStringProperty(::jwt.name) | ||
private set(value) { | ||
setStringProperty(::jwt.name, value!!) |
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.
This !!
is dangerous, since the constructor for this class just below says jwt
passed in can be null
, and it will call this set method directly within it.
…ion specific operation This commit serves as an example of implementing JWT for a specific backend service. To enable JWT for any backend service, follow these steps: 1. Find an OperationExecutor (e.g., SubscriptionOperationExecutor.kt). 2. Locate any BackendService contained within this OperationExecutor (e.g., _subscriptionBackendService). 3. In the BackendService, examine all its methods. Add jwt: String? = null if the method is one of the authorized operations in the JWT dev documentation (e.g., createSubscription()). Make sure to modify both ISubscriptionBackendService.kt and SubscriptionBackendService.kt. 4. In the implementation of the BackendService (e.g., SubscriptionBackendService.kt), locate all HTTP client calls such as _httpClient.post / get / patch, and add jwt as the last parameter. 5. Return to the OperationExecutor (e.g., SubscriptionOperationExecutor.kt). If _identityModelStore is not a property, add private val _identityModelStore: IdentityModelStore as a property. 6. Update parameters in all backend service calls with _identityModelStore.model.jwtToken at the end."
…entity model store
…ion specific operation This commit serves as an example of implementing JWT for a specific backend service. To enable JWT for any backend service, follow these steps: 1. Find an OperationExecutor (e.g., SubscriptionOperationExecutor.kt). 2. Locate any BackendService contained within this OperationExecutor (e.g., _subscriptionBackendService). 3. In the BackendService, examine all its methods. Add jwt: String? = null if the method is one of the authorized operations in the JWT dev documentation (e.g., createSubscription()). Make sure to modify both ISubscriptionBackendService.kt and SubscriptionBackendService.kt. 4. In the implementation of the BackendService (e.g., SubscriptionBackendService.kt), locate all HTTP client calls such as _httpClient.post / get / patch, and add jwt as the last parameter. 5. Return to the OperationExecutor (e.g., SubscriptionOperationExecutor.kt). If _identityModelStore is not a property, add private val _identityModelStore: IdentityModelStore as a property. 6. Update parameters in all backend service calls with _identityModelStore.model.jwtToken at the end."
…entity model store
868d37b
to
95ec9c2
Compare
READ AND DELETE THIS SECTION BEFORE SUBMITTING PR
Description
One Line Summary
Implement identity verification functionality to our Android SDK with JWT (JSON Web Token) that manage a specific User, their Subscriptions, and Identities, if enabled using OneSignal dashboard.
Details
Motivation
OneSignal Identity Verification feature only exists today for the Player model and we want to bring this feature to the User Model and gives us an opportunity to switch to a more standard client security model, JWT (JSON Web Token).
Scope
If identity verification is enabled, all operations requiring identity or authorization must be accompanied by a correct JWT. Developers can use OneSignal.login(external_id, jwt) to set a JWT for a specific user identified by their external ID, or OneSignal.updateUserJwt(external_id, jwt) to update the JWT for that user. Additionally, developers can add a JWTInvalidatedListener by calling OneSignal.addUserJwtInvalidatedListener, allowing them to listen for a JWTInvalidatedEvent triggered when a JWT is invalidated by any operation, and subsequently update the JWT as necessary.
Testing
Unit testing
I have include a testing case that simulate a UNAUTHROIZED error (401, 403) and ensure that the jwt for the specific user is invalidated and the JWTInvalidatedEvent is fired.
Manual testing
!!! Note that we are unable to manual test this feature at the moment due to the backend service is not completed. However, I have tested other normal functionalities to ensure the code does not break for existing users.
Affected code checklist
Checklist
Overview
Testing
Final pass
This change is