Open nickevansuk opened 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.
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.
Nick's summary of Debbie's thoughts:
On the call today we discussed the three different types of "authentication" available via the Open Booking API following this proposal.
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 (Offer
s) to be published through the open feeds, and for Brokers to be given permission by the Seller (via their contractual terms) to use these Offer
s when booking. It is the Broker's responsibility to choose the appropriate price level for the Customer.
Example usecases:
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:
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:
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?
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"?
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?
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?
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!
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.
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 anavailableChannel
ofhttps://openactive.io/OpenBookingCustomerAuthentication
, and does not need to include anavailableChannel
ofhttps://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.
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.
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.
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.
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"
},
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.
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
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".
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.
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:
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 subclassesPerson
. ThisAuthenticatedPerson
can be used as both acustomer
andattendee
, in place of a "guest"Person
, and alongside other "guest"Person
s.The
AuthenticatedPerson
type includes an additionalaccessToken
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, aaccessToken
is usually short lived (e.g. 1-2 hours).The
AuthenticatedPerson
must be valid against theOrderItem
for which it is used (ifattendee
), or valid against allOrderItem
s (ifcustomer
). If this is not the case, a specific subclass ofOpenBookingError
should be returned against theOrderItem
, or wholeOrder
, respectively.Classes
oa:AuthenticatedPerson
schema:Person
Properties
oa:AuthenticatedPerson
)oa:accessToken
schema:Text
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
, usingemail
andidentifier
properties as appropriate. Additional properties ofPerson
MAY also be supplied, such asgivenName
andfamilyName
. 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, theaccessToken
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, thecode
flow of Open ID Connect is strongly recommended, and theimplicit
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
vsaccess_token
In the OpenID Connect specification, the intended audience of the
id_token
is the client application (Broker), where the intended audience of theaccess_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 theaccess_token
is the relevant token to use here. Theaccess_token
also contains the minimum personally identifiable information by default.OpenID Connect
id_token
vs/userinfo
viaaccess_token
Within OpenID Connect there are two standard options for getting the authenticated user's details: the OpenID Connect (OIDC) specification includes:
refresh_token
was used./userinfo
endpoint accessible via anaccess_token
access_token
, which can be extended via therefresh_token
.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 theid_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:
C1 , C2 and B response: