Skip to content
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

Closed
wants to merge 12 commits into from

Conversation

jinliu9508
Copy link
Contributor

@jinliu9508 jinliu9508 commented Mar 15, 2024

READ AND DELETE THIS SECTION BEFORE SUBMITTING PR

  • Fill out each REQUIRED section
  • Fill out OPTIONAL sections, remove section if it doesn't apply to your PR
  • Read and fill out each of the checklists below
  • Remove this section after reading

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

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
    • If it is hard to explain how any codes changes are related to each other then it most likely needs to be more than one PR
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
    • Simplify with less code, followed by splitting up code into well named functions and variables, followed by adding comments to the code.
  • I have reviewed this PR myself, ensuring it meets each checklist item
    • WIP (Work In Progress) is ok, but explain what is still in progress and what you would like feedback on. Start the PR title with "WIP" to indicate this.

This change is Reviewable

@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from 268d1a7 to 97e4b8a Compare March 15, 2024 05:42
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch 3 times, most recently from 81ebe0b to ee6ff69 Compare March 28, 2024 18:23
@jinliu9508 jinliu9508 changed the title WIP: Identity verification implementation Identity verification implementation Apr 1, 2024
Copy link
Contributor

@nan-li nan-li left a 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
Copy link
Contributor

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))
Copy link
Contributor

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
Copy link
Member

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.

@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from cb7a790 to 3238d9a Compare April 9, 2024 18:10
@jinliu9508 jinliu9508 changed the title Identity verification implementation [Feature] Identity verification implementation May 2, 2024
@jkasten2 jkasten2 added the WIP Work In Progress label May 3, 2024
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from 22fe20f to 4d1a0b4 Compare May 15, 2024 17:56
)
) {
fun invalidateJwt() {
model.setStringProperty(
Copy link
Contributor

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!!)
Copy link
Contributor

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.

@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from 868d37b to 95ec9c2 Compare May 17, 2024 17:28
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch 2 times, most recently from 52edda4 to 108b627 Compare June 3, 2024 17:25
Comment on lines 524 to 525
// remove identification required operations in the queue
operationRepo!!.removeIdentificationRequiredOperations()
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is the right high level concept here. We want to remove any operations called before OneSignal.login(). If addTags() are call right after login we want to keep it, but if it was call before we want to discard.

  • An edge case that comes to mind which is tricky is subscription updates, such as the user accepting the notification permission.

* in order to receive control when the params has fetched on the current device.
*/
interface IParamsBackendServiceObserver {
fun onParamsFetched(params: ParamsObject)
Copy link
Member

@jkasten2 jkasten2 Jun 6, 2024

Choose a reason for hiding this comment

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

I notice we have a ConfigModel that has subscribe and unsubscribe methods. I see some of the remote params are on ConfigModel. I am not sure why all remote level params didn't go through the ConfigModel to begin with, but now sounds like a good time to consolidate, if it is a good fit.

@@ -101,6 +103,13 @@ internal class OperationRepo(
}
}

fun <T : Any> containsParameterOfType(
Copy link
Member

Choose a reason for hiding this comment

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

(ignore if this is just a test function we delete later) Shouldn't this be called getParametersOfType and return a list?

@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from bcdc0f3 to c8388e6 Compare July 30, 2024 14:22
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from c8388e6 to 0bf69c9 Compare August 13, 2024 19:20
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from 0bf69c9 to 42dced1 Compare August 26, 2024 16:56
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from 42dced1 to fe688e3 Compare August 26, 2024 17:00
@jinliu9508 jinliu9508 force-pushed the identity-verification-implementation branch from a3d47a7 to c4590fa Compare September 19, 2024 17:17
@jinliu9508
Copy link
Contributor Author

Close and re-create another PR for this feature.

@jinliu9508 jinliu9508 closed this Sep 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WIP Work In Progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants