openactive / open-booking-api

Repository for the Open Booking API specification
Other
2 stars 3 forks source link

Customer Authentication #120

Open nickevansuk opened 4 years ago

nickevansuk commented 4 years ago

Proposer

MCRactive, Westminster City Council, Everyone Active, Gladstone, Playwaze, SportBuddy, Deuce, Bristol City Council, London Sport, Played, Sport:80

Reference email threads: 1, 2, 3

Use Case

As an existing member of Everyone Active, I want to be able to use my membership through MCRactive, Active Westminster, Deuce, and SportBuddy to find and book activities and facilities, ensuring that I pay prices that include any membership discounts (include discounted to "free" if appropriate).

This is particularly useful to the activity provider for retention-based use cases (e.g. brokers that provide health tracking or motivational tools).

Why is this needed?

The existing booking specification includes a recommendation for "11.7.3 Customer level authentication”, but no mechanism to allow these details to be used as part of the booking flows, except for an example extension which does not include enough information to fully specify an implementation.

User journey

1) An anonymous Customer using a Broker is prompted to choose between "Guest Checkout" and "Use my existing membership".

- To ensure this journey is smooth, the Broker will need access to the Customer's e-mail address to send e-mail notifications.

2) A logged-in Customer using a Broker is prompted to "Connect my existing membership" so that subsequent purchases can make use of that membership where relevant.

- Although as per [16.6.1 Simplified VAT invoices](https://www.gov.uk/guidance/vat-guide-notice-700#information-required-on-a-vat-invoice) a customer's details are not required on the VAT invoice for the simple case, it may be useful to have basic personal data (e.g. e-mail address) available to the Broker for the purposes of accurate record keeping - i.e. so that the VAT invoices created by the Broker are not incorrectly attributed.

Proposal

Open Booking API 1.0 CR section 11.7.3 currently recommends the popular standard OpenID Connect for Customer Authentication. The OpenID Connect flow is the same flow that is used for "Login with Facebook" or "Login with Google" that many users are already familiar with:

Untitled drawing (5)

This proposal recommends building on this existing recommendation with a mechanism to allow this authenticated Customer to make a booking.

Model additions

This can be achieved with minor updates to the existing specification, via the introduction of a new AuthenticatedPerson type that subclasses Person. This AuthenticatedPerson can be used as both a customer and attendee, in place of a "guest" Person, and alongside other "guest" Persons.

The AuthenticatedPerson type includes an additional accessToken property, which is used to demonstrate the Broker's authorization to book on behalf of the Customer, and also to identify that Customer. Following Open ID Connect conventions, a accessToken is usually short lived (e.g. 1-2 hours).

The AuthenticatedPerson must be valid against the OrderItem for which it is used (if attendee), or valid against all OrderItems (if customer). If this is not the case, a specific subclass of OpenBookingError should be returned against the OrderItem, or whole Order, respectively.

Classes

Class subClasses Description
oa:AuthenticatedPerson schema:Person Represents a Person that has authenticated with the Seller, and granted access to the Broker to book on their behalf.

Properties

(Class) Property Expected Type Description
(oa:AuthenticatedPerson)
oa:accessToken
schema:Text An access token that verifies the Broker's authorisation to book on behalf of a specific Customer.

Restricted access to Booking System personal data

To inform the Customer that their linked account has been used to create the Order, for the purposes of audit and attribution, to take payment, and to allow for e-mail notifications to be sent in user journey (1) a minimal set of fields of personal data are required.

The proposal recommends that the customer's e-mail (and membership identifier, if different from e-mail) MUST be supplied in the response to C1 , C2 , and B within AuthenticatedPerson, using email and identifier properties as appropriate. Additional properties of Person MAY also be supplied, such as givenName and familyName. The OpenID Connect flow will capture explicit consent from the customer to share this data, which mitigates any concerns around GDPR.

To ensure minimum exposure of the personal data that the Customer has agreed to share, the accessToken MUST be either an opaque token (that contains no personal data) or an encrypted JWT (that contains only the Customer's unique identifier, and is encrypted). If an encrypted JWT is used, the accessToken MUST NOT be stored for longer than its expiry (typically 1-2 days).

In order to ensure that the personal data received is not used outside of the bounds of providing the service, this personal data MUST NOT be used for any other purpose outside of generating invoices, VAT receipts and notifications relating specifically to that unique Order. For the avoidance of doubt, it MUST NOT be used for creation of an account in the Broker, or for other communication outside of those required to service that specific Order, without the Seller's explicit consent (and Customer's explicit consent, where GDPR requires it).

Security

Due to the ability for B to return personal data from the Booking System, which relies on the accessToken being supplied from the results of user authentication, the code flow of Open ID Connect is strongly recommended, and the implicit flow RFC6749 MUST NOT be used (ref).

Offline access

In order to allow the Broker to make bookings without requiring continual re-authentication of the Customer, Offline Access MUST be permitted, where the user has explicitly consented (as per OpenID Connect spec), using a refresh_token.

Pricing

The prices returned for the Offers requested by the Broker on behalf of the Customer MUST match the expectations of the Customer based on any existing membership with the Seller.

Broker contract with Booking System

Currently "The Broker MUST call C1 (without Customer details)", however with this proposal the Broker MAY supply the AuthenticatedPerson at C1 , if known, to have C1 return the correct pricing.

Open Questions and Design Decisions

Access to additional personal data

It is possible for the specification to recommend a means for Brokers to access additional personal data about the Customer, via the OpenID Connect /userinfo endpoint of the Booking System. A Booking System MAY make a number of claims available to the Broker via this mechanism within the Open ID Connect specification.

Do we want to recommend use of /userinfo within the Open Booking API?

To keep the core of Open Booking API cleanly separated from the authentication approach, to adopt a privacy-first approach, and to ensure that the API remains applicable to the broadest number of use cases - the proposal currently recommends that it is better to not include reference to /userinfo specifically in the Open Booking API specification, and certainly not to require it for the flow.

OpenID Connect id_token vs access_token

In the OpenID Connect specification, the intended audience of the id_token is the client application (Broker), where the intended audience of the access_token is the resource server (Booking System). The token for this use case is primarily intended to be received by the Booking System, to provide access to book on the user's behalf. Hence the access_token is the relevant token to use here. The access_token also contains the minimum personally identifiable information by default.

OpenID Connect id_token vs /userinfo via access_token

Within OpenID Connect there are two standard options for getting the authenticated user's details: the OpenID Connect (OIDC) specification includes:

Due to the possibility of token substitution attacks, the /userinfo response is not guaranteed to be about the End-User identified by the sub (subject) element of the ID Token. "The sub Claim in the UserInfo Response MUST be verified to exactly match the sub Claim in the ID Token; if they do not match, the UserInfo Response values MUST NOT be used.". Hence the id_token is required even with access to /userinfo.

As above, perhaps it is better to not recommend either of the above specifically, and not to require them in the flow.

Example

C1 , C2 and B request:

  "customer": {
    "@type": "AuthenticatedPerson",
    "accessToken": "SlAV32hkKG"
  },

C1 , C2 and B response:

  "customer": {
    "@type": "AuthenticatedPerson",
    "identifier": "314251861",
    "email": "geoffcapes@example.com",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
nickevansuk commented 4 years ago

Discussing with Debbie at Everyone Active, she's pointed out that Westminster are looking for a custom set of fields for the verification, and hence an implementation may need to support more than one set of credential options, e.g. "Member ID and Pin", "Card Number and Last Name", "E-mail address and Password" etc.

Untitled drawing (6)

Also note that the above would be useful for booking where there is a restriction in place, to allow existing members to book e.g. "Casual Gym" or "Climbing Wall" (which require an induction). These would not be available to normal guest checkout customers, so we would need a way of differentiating those activities that require an account or Customer Authentication vs those that are ok to book as guest?

So in the open data, could we have a flag or similar to say "requires Customer Authentication to book"? Could use availableChannel for this, for example with a https://openactive.io/OpenBookingCustomerAuthentication?

E.g. for Casual Gym there's an online booking video on the Everyone Active website.

Also note that the advanced booking period may be different for logged in customers compared with guests, so the open data would need to indicate this? Within Gladstone this is by price level / subscription, so it will vary wildly between customers - and so perhaps e.g. a simple membership-only version of oa:validFromBeforeStartDate is too simple?

Also note you'd need to display the privacy policy of the broker in the same window along with their consent.

The validation page also needs the ability to block login if too many failed login attempts have occurred, for security.

nickevansuk commented 4 years ago

Nick's summary of Debbie's thoughts:

nickevansuk commented 4 years ago

Types of "Authentication"

On the call today we discussed the three different types of "authentication" available via the Open Booking API following this proposal.

Customer Authentication W3C discussion

Option 1 - Broker Login

Pricing available: Rationalised discount pricing end-to-end

This is already possible with the current Open Booking API specification. The Customer has an account with the Broker (e.g. MCRactive or Change4Life), and uses the information they have already stored within that account to make a "Guest Checkout" booking with the Seller ("provider") via the Booking System.

This would be used for casual participants within Active Westminster (Phase 2) and MCRactive (Phase 1), as well as apps like Decathlon Play that include a login.

Using this method it is possible for a limited ("rationalised") set of discount prices (Offers) to be published through the open feeds, and for Brokers to be given permission by the Seller (via their contractual terms) to use these Offers when booking. It is the Broker's responsibility to choose the appropriate price level for the Customer.

Example usecases:

Option 2 - One-off Provider Account Login

Pricing available: Membership pricing at checkout only (not during "Select" / Search)

The Customer does not have an account with the Broker, and is presented with the option to either (i) "Guest Checkout" or (ii) "Login with Everyone Active".

This would be used for e.g. Phase 1 of Active Westminster (white-labelled to "Login with Active Westminster"), or for apps like Change4Life where a parent might find an activity for their child that they hadn't realised was already part of their existing membership.

Once the booking is made, the Customer can cancel it via e.g. the confirmation e-mail link sent by the Broker (using the standard "Customer requested cancellation" flow), or through the existing account within the Booking System (which triggers the existing second half of the "Customer requested cancellation" flow as per the existing specification).

Example usecases:

Option 3 - Connect My Provider Account

Pricing available: Membership pricing at checkout only (not during "Select" / Search)

The customer has an account with Broker, and wishes to "link" their monthly membership so that they can place bookings with the Seller using their existing membership account.

There is not a two-way flow of bookings, so the Broker would only have visibility of bookings made through that Broker (not all bookings). All bookings would appear in the Customer's existing account within the Booking System.

Example usecases:

Further questions raised

The customer journey for (2) and (3) does not display member pricing until checkout, is this an issue?

N.B: Rationalised discount card prices are possible, see Option 1 above.

As the OpenActive architecture is based primarily on open data feeds, it is not possible within the current specifications to display bespoke members pricing in the search / select stage of the customer journey. Such a feature would require a separate "Opportunity API", which has been previously discussed in the OpenActive community, but is not on the short or medium term roadmap of OpenActive standards at present. This is unrelated to the Open Booking API, and could work alongside it to present accurate member pricing at the point of search. Note that such an Opportunity API would be less useful for displaying opportunities from a number of providers, and more useful for displaying accurate membership pricing for a single provider.

For the first version of this Customer Authentication, is it sufficient to not have member pricing available until checkout? Or is it better to delay Customer Authentication until a date in the future when an Opportunity API may also be available?

What about Opportunities that are only available to members, or a subset of members?

All bookable activities need to be available as open data, though some can be marked as "membership required" (https://github.com/openactive/modelling-opportunity-data/issues/80), this proposal does not cover activities that are exclusively available to some members (e.g. only "Gold" memberships). An Opportunity API, as above, would be required to have detailed membership pricing and eligibility available during browse.

How much of an issue is this with existing providers? Are many activities "exclusive"?

What would a customer expect in terms of communication?

If a user has booked through Change4Life using their existing provider membership, the Change4life user would receive messages related to this booking from the Broker (Change4Life) instead of the Provider direct (as they have paid via the Broker). If they cancel within the Booking System or the Broker, the communication must come from the Broker in both cases, along with the refund processed (triggered using the the "Customer requested cancellation" flow).

Would this match the user’s expectations?

Which Offer should a broker use?

If a number of “discount card” prices are available, how does the Broker differentiate between Offers? For Brokers and Data Users who are looking to display a single adult headline price, or a single child headline price, which do they choose?

Do we need to add a standard vocabulary to describe adult and child, and “discount hard required” (with a link to where one can acquire the discount card) so that all data users can include that in their user experiences?

nickevansuk commented 4 years ago

Discussion update

This comment summaries the progress made in the discussion to date with concrete recommendations for changes to the specification that aim to satisfy all stakeholders. Further comments very welcome!

The customer journey for (2) and (3) does not display member pricing until checkout, is this an issue?

SUMMARY: Member pricing is not displayed before checkout

Discussions on this topic appear to have concluded that - given the extremely high technical complexity of displaying granular member pricing before checkout - it is not a priority for now, and hence is not something that we need to resolve as part of this proposal. This is primarily because most brokers are not concerned with supplying member pricing, as they are focussed on aggregation across a number of providers rather than accurate membership pricing for a specific provider.

Hence we can move displaying member pricing during search out-of-scope of this proposal.

Note however that rationalised discount pricing can still be achieved through the existing specification via Offers (Option 1 above); it is only bespoke membership pricing that is out-of-scope.

What about Opportunities that are only available to members, or a subset of members?

SUMMARY: Each opportunity can simply have "Book Now" and "Book with your Everyone Active membership" buttons, that are visible as appropriate. No details are displayed about which specific memberships the opportunity is applicable to, as this would likely be very complex to display anyway.

If we assume that bespoke membership pricing is out-of-scope here, as above, we only really need to differentiate between opportunities bookable via guest checkout, and opportunities that require a membership and hence Customer Authentication.

This proposal suggests that we use availableChannel for this, for example with a value of https://openactive.io/OpenBookingCustomerAuthentication.

The specification would be amended to include wording such as:

For a 'bookable' Opportunity and Offer pair to qualify for Customer Authentication, the relevant Offer MUST include an availableChannel of https://openactive.io/OpenBookingCustomerAuthentication, and does not need to include an availableChannel of https://openactive.io/OpenBookingPrepayment.

This allows us to leave the "membership required" (openactive/modelling-opportunity-data#80) properties in beta for now as (i) we are not currently displaying membership pricing, as above (ii) the actual requirement here is more subtle than "membership required", as it's about whether the opportunity is bookable via guest checkout or via customer authentication, or both.

Which specific membership is required for this particular opportunity can be handled separately as part of this discussion: https://github.com/openactive/open-booking-api/issues/45. For most practical purposes, most sessions are available to most memberships - it is just the price that varies.

What would a customer expect in terms of communication?

SUMMARY: The Broker communicates all updates to the Customer for bookings made via the Broker, even if amendments are made from within the Booking System. Bookings made via the a Broker can only be updated in two specific ways.

This proposal attempts to satisfy all stakeholders minimally: minimising the implementation overhead while also ensuring the Customer experience is consistent.

To meet Customer expectations, all bookings made through the Open Booking API via Customer Authentication SHOULD be visible to the Customer within their account within the Booking System.

Additionally, the Booking System MAY allow the Customer to perform the following operations from within the Booking System on bookings made originally through the Open Booking API via Customer Authentication:

In both cases, as per the rules in the Open Booking API specification, and the Booking System MUST NOT send notifications to the customer directly, and the Broker MUST process any refund due and send immediate notifications to the Customer. This makes the communication consistent within the scope of each booking: if a Customer booked through the Change4Life app, all further updated would come through the same app, which keeps the app in a consistent state.

The Booking System MUST NOT allow the user to perform any other actions on the booking from within their user experience, as these are not supported by the Open Booking API, and therefore will not be supported by the Broker, and would create an inconsistent state. The user interface of the booking system SHOULD reflect this, and reference the name property of the broker for that booking, such that it communicates "Booked via Broker X".

These proposed changes should be in keeping with the Customer's expectations, and the communication from the Broker should be expected if bookings are labeled in the interface.

Which Offer should a broker use? If a number of “discount card” prices are available, how does the Broker differentiate between Offers?

SUMMARY: Codifying entitlements can be covered separately

This functionality is separate from Customer Authentication, and should therefore be broken into a separate issue.

In the first instance this can be agreed out-of-band between the Broker and the Seller using the identifier of the Offer as a reference.

Codifying entitlements so that this data can be published can then be picked up separately.

Updates to the model

To achieve the above, the property accessToken should be defined on the type AuthenticatedPerson that subclasses Person, used as per the example below.

OpenID Connect is used to attain the accessToken, as described here.

Example

The below example would be identical for both customer and attendee scenarios.

C1 , C2 and B request:

  "customer": {
    "@type": "AuthenticatedPerson",
    "accessToken": "SlAV32hkKG"
  },

C1 , C2 and B response:

  "customer": {
    "@type": "AuthenticatedPerson",
    "identifier": "314251861",
    "email": "geoffcapes@example.com",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
nickevansuk commented 4 years ago

Additional consideration for error conditions should be included in the revision based on the proposal above.

This can be achieved by simply defining subclasses of OpenBookingError in the model to cover, for example, where an authenticated Customer is denied a booking (e.g. an existing member has booked some stuff in the past but not turned up so has a flag or penalty fee associated with their account that remains unpaid).

A customer-facing description should be included in such errors.

nickevansuk commented 3 years ago

For those interested in following progress on the discussion around this proposal, a Google document has been created with a first draft of the specifics:

https://docs.google.com/document/d/1mjmb-si95H_YK9qeNIhyTBfstGI-2NPAycz0y78GYGc/edit

Please do comment on this issue or get in touch directly if you are interested in participating