A mailer, member database, and so much more, for digital activism.
In order for a user to make use of an organisations platform, and in order for an organisation to store and process a users details, the user must consent to legal terms and conditions. These terms will differ by country and organisation, but as well as the terms themselves, the method of obtaining consent will be different. This is due to different laws and requirements around the world.
For example, in some countries and jurisdictions it may be perfectly legal for an organisation to simply display text saying “by submitting your details, you consent to our terms of service”. In others, a pre-checked tickbox saying “I agree to the terms of service” may be the minimum required, and in others still it would not be legal to pre-tick such a checkbox.
Within Identity there are four levels of consent:
There is also a concept of ‘No Change’, which usually means no consent was obtained on the basis that the user already consented in the past.
Examples of the different levels of consent:
A ConsentText
object, stored in the consent_texts
table, represents an immutable legal text which a user may consent to.
The fields are:
public_id
- A unique string which may be used internally & externally to identify this particular version of a legal text. Eg. terms_of_service_1.0
, privacy_policy_2.1
, donations_policy_1.0.1
, etc.consent_short_text
- The short text displayed to the user when they were submitting their details. Eg. I agree to the <a href="open.org/terms_of_service_1.0">terms of service</a>
full_legal_text_link
- A link to the full legal text. This link should obviously point to an immutable HTML page. How this page is made available and immutable is beyond the scope of Identity and of this documentation.A MemberActionConsent
object, stored in the member_action_consents
table, represents all ConsentText
s a user agreed to when they took an action. The level of consent obtained will often be ‘No Change’ because in most countries / jurisdictions there will be no need to force a user to re-state their consent every time they take action, although it’s possible this could be a requirement in future.
The fields are:
member_action_id
- Links to the MemberAction
object when this consent was obtained.consent_text_id
- Links to the ConsentText
object which the user consented to.consent_level
- The level of consent obtained as discussed in the ‘Levels of Consent’ section.consent_method
- (Optional) Free text representing the input method used to obtain consent. Eg. checkbox
, dropdown
, radiobuttons
, etc.consent_method_option
- (Optional) Depending upon the consent_method
, there may be text associated with each option. For example, we may show text saying “I agree to the terms”, and then a dropdown with “Yes” and “No” options. In such a case this field stores the “Yes” or “No”. These will also be mapped to a consent_level
, but storing the raw text may be legally important!parent_member_action_consent_id
- (Optional) If the consent_level
is no_change
, this may optionally be set to the id of the last consent for the same ConsentText
(ie. the users effective consent at the time the action was taken).A member’s current consent is now determined by taking the latest ‘no_change’ consent for each consent text from the member_action_consents table.
Identity stores all data related to users, but will not actually be able to capture consent for a user. That is left to external applications, most commonly this will be Speakout or ControlShift (or both).
These apps may choose not to re-obtain consent from a user every time they take action, assuming the user has already consented to the required terms in the past.
The following diagrams show how Speakout will operate to show only relevant consents to users. The exact logic which Speakout or other apps use to determine whether to show a consent or not is beyond the scope of this documentation, but it is assumed that consent for the exact same ConsentText
will not be obtained multiple times unless a higher level of consent is now required (eg. previously implicit
consent was allowed, but now explicit_opt_in
consent is required).
See the Identity API docs for more background on using the Member Details and Actions APIs.
The existing API to find out a members data is used (see app/controllers/api/members.rb
). This is called by executing a POST on /api/member/details
, passing in the guid
or email
of a user. If the current consents for a user are also required, an additional parameter, load_current_consents
must also be passed in, and the method will add to the hash object a list of the current consents in the following format:
{
"existing_fields...": "...",
"consents": [
{
"public_id": "terms_of_service_1.0",
"consent_level": "explicit_opt_in",
"consent_created_at": "2016-12-30 23:30:13 +0000"
},
{
"public_id": "privacy_policy_2.6",
"consent_level": "implicit",
"consent_created_at": "2015-01-01 10:10:10 +0000"
}
]
}
The existing API to send actions from external systems to Identity remains in use. This now optionally accepts a consents
field in the json payload, which should contain a list of all consents required in order to take the given action. Note that even if a consent option is not displayed to the user because they have opted in previously, if the action requires consent,it should still be listed in the API with a ‘no_change’ consent_level
. The format is as follows:
{
"existing_fields...": "...",
"consents": [
{
"public_id": "terms_of_service_1.0",
"consent_level": "no_change"
},
{
"public_id": "privacy_policy_2.0",
"consent_level": "explicit_opt_in",
"consent_method": "dropdown",
"consent_method_option": "Yes, I accept"
},
{
"public_id": "donations_policy_1.6",
"consent_level": "none_given",
"consent_method": "checkbox"
}
]
}
Once the migrations to add the new database tables is done, you simply need to create the appropriate ConsentText
instances in the database. There is currently no way to create a new ConsentText
(ie. some text that people will be consenting to) other than through the backend. If another system such as Speakout or ControlShift sends Identity a consent with a public_id
which Identity doesn’t recognise, it will throw an error.
Creating a new ConsentText
can be done in the Padrino/Ruby console as follows:
ConsentText.create!(
public_id: 'privacy_policy_v1',
consent_short_text: 'I consent to the <a href="https://{ your website }/privacy_policy_v1" target="_blank">privacy policy</a>',
full_legal_text_link: 'https://{ your website }/privacy_policy_v1'
)
Once you have created a ConsentText in Identity, other systems such as Speakout & ControlShift can send the public_id
of this consent to Identity. This will involve creating the ConsentText and any other required configuration in the other systems.
IMPORTANT NOTE: The consent_short_text
and full_legal_text_link
for a public_id
must be identical in all systems (Identity, Speakout, ControlShift, etc)! How this is managed and guaranteed is beyond the scope of this documentation.
At this point a consent has been created in Identity, but the same consent must be configured in other systems such as Speakout, Controlshift, etc in order for anything useful to happen. Just having a ConsentText
setup in Identity and other linked systems will simply record whether the user opted in or out of the consent text - it won’t actually do anyting in response to a user opting in or out! So additionally, if you want something to happen when a user opts in or out (such as subscribe them to email communications for example) you also need to setup one or more PostConsentMethod
entries. See the Updating Consent Texts documentation for more detail on doing this, as well as setting up consents in different linked systems.
Identity may store all consents sent by external applications whenever an action is taken by a user (including ‘no_change’ consents). This is turned off by default. It can be enabled by overriding the value of Settings.consent.record_no_change_consents
for your org.
Admins can request all data related to a single member, for example in response to a request from that member under EU GDPR regulations. On a member page /members/:id
click the Export Data button and you can select between mailing a (password protected) archive to either the email of the logged-in admin or the email of the member. Export emails will be sent using the email address defined in setting.yml...transactional_member_emails
There are two environment variables to be set along with this feature:
MEMBER_DATA_EXPORT_LISTS
include list membership in the report. defaults to falseMEMBER_DATA_EXPORT_EMAIL_PASSWORD
email to member will include the unencrypted password. defaults to false.CSL can offer members additional custom consent questions outside of privacy and contact consents, e.g. asking for peoples age bracket to support ‘Age Appropriate Design’ in the UK.
If you want to use additional custom questions on your CSL instance you will need to:
ConsentText
which matches the question text in IDPostConsentMethods
in ID as necessary (ie. if you want a person responding in a particular way to the question to have some impact other than just being recorded - for example, you want to subscribe or unsubscribe someone from a particular subscription)ControlshiftConsent
, using the desired question slug as the controlshift_consent_external_id
ControlshiftConsentMapping
, which maps from the ControlshiftConsent
to the ConsentText
and uses the appropriate consent_level
and consent_method_option
depending upon whether the user opted in or outadditional_fields
hash and should match what was used in the ControlshiftConsent
table, (c) the possible values for the answer which will be shown to the user, as well as which answer is true
and which is false
- true
or false
will appear as the value in the additional_fields
hashmember_action_consents
table).