Commit | Line | Data |
---|---|---|
9e99ef4a TO |
1 | # AuthX |
2 | ||
3 | The AuthX extension provides support for additional authentication protocols on CiviCRM pages. | |
4 | This is useful for automated testing and developing multi-modal pageflows (eg email => HTTP). | |
5 | ||
6 | ## Overview | |
7 | ||
b9cbf1a6 | 8 | There are two general flows of authentication, each with a few variations: |
9e99ef4a TO |
9 | |
10 | * __Ephemeral / Stateless__: The client submits a singlular request (such as an API call) which includes credentials. The request is authenticated and processed, but then it is abandoned. | |
11 | There are a couple flavors of stateless authentication: | |
12 | * __Parameter (`param`)__: The credential is submitted with an HTTP GET or POST parameter (`?_authx=<credential>`) | |
13 | * __Common Header (`header`)__: The credential is submitted with an HTTP (`Authorization:`). Depending on the credential, this will use either `Basic` or `Bearer`. | |
14 | The standard header is easier to integrate with external applications; however, other features or plugins in the CMS may interfere with correct operation. | |
15 | * __X-Header (`xheader`)__: The credential is again submitted with an HTTP header (`X-Civi-Auth:`). The header behaves the same as the common header. The | |
16 | differing name means that clients must specifically support it, but it also reduces the odsd of interference. | |
17 | * __Persistent / Stateful__: The client makes a request for a persistent session, attaching the contact ID and/or user ID. These will be used in subsequent requests. | |
d0528c96 | 18 | * __End-point session (`login`)__: The client submits an explicit authentication request (`POST /civicrm/authx/login?_authx=<credential>`) which creates a session and cookie. |
9e99ef4a TO |
19 | The authenticated session endures until one logs out (`/civicrm/authx/logout`). |
20 | * __Auto session (`auto`)__: The clients submits a GET request for any page (`?_authx=<credential>&_authxSes=1`). The session is initialized. The user redirects | |
21 | to original page. | |
22 | ||
23 | Each flow may (configurably) accept any of these credentials: | |
24 | ||
25 | * __JSON Web Tokens (`jwt`)__: These tokens must be generated by Civi using a cryptographic signing key and expiration time. | |
26 | The client submits the token as its sole credential. | |
27 | * __API Key (`api_key`)__: The user configures a value for `civicrm_contact.api_key`. The client submits | |
28 | the token as its sole credential. | |
29 | * __Username/Password (`pass`)__: The client submits the username and password for a CMS user. | |
30 | ||
31 | CiviCRM `Contact` records are often linked to CMS `User` records -- but not always. There are different ways to approach this matching: | |
32 | ||
33 | * __Ignore__: Ignore any related `User` accounts. From a CMS perspective, the user is anonymous. | |
34 | * __Optional__: If there is a correlated CMS `User`, then load it. If there isn't, leave the CMS user as anonymous. | |
35 | * __Require__: Only allow authentication if proceed if there is a correlated user account. | |
36 | ||
37 | ## Configuration | |
38 | ||
39 | For each authentication flow, one may toggle support for different credentials and user-links. Here is the default configuration: | |
40 | ||
41 | * Ephemeral: Parameter flow | |
42 | * Accepted credentials (`authx_param_cred`): `['jwt']` | |
43 | * User link (`authx_param_user`): `'optional'` | |
44 | * Ephemeral: Common header flow | |
45 | * Accepted credentials (`authx_header_cred`): `['jwt']` | |
46 | * User link (`authx_header_user`): `'optional'` | |
47 | * Ephemeral: X header flow | |
48 | * Accepted credentials (`authx_xheader_cred`): `['jwt']` | |
49 | * User link (`authx_xheader_user`): `'optional'` | |
50 | * Persistent: End-point session flow | |
d0528c96 TO |
51 | * Accepted credentials (`authx_login_cred`): `['jwt']` |
52 | * User link (`authx_login_user`): `require` | |
9e99ef4a TO |
53 | * Persistent: Auto session flow |
54 | * Accepted credentials (`authx_auto_cred`): `['paramalogin']` for Joomla, and `[]` for all others | |
55 | * User link (`authx_auto_user`): `require` | |
56 | ||
57 | ## Quirks and Compatibility | |
58 | ||
59 | Some modes may not be supported in some environments. For example: | |
60 | ||
61 | * __Joomla, WordPress__: Creates extraneous sessions (e.g. even if the request is for stateless authentication). | |
62 | * __WordPress__: The default `.htaccess` in WordPress may obscure the common `Authorization:` header. Fix this by adding: | |
63 | ``` | |
64 | SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 | |
65 | ``` | |
66 | ||
67 | ## Examples | |
68 | ||
69 | ### JSON Web Token | |
70 | ||
b9cbf1a6 TO |
71 | By default, JSON Web Tokens are accepted for authentication in all flows. |
72 | ||
73 | To use JWT authentication, you must first prepare a token on the server: | |
74 | ||
9e99ef4a | 75 | ```php |
b9cbf1a6 TO |
76 | $token = Civi::service('crypto.jwt')->encode([ |
77 | 'exp' => time() + 5*60, // Expires in 5 minutes | |
78 | 'sub' => 'cid:203', // Subject (contact ID) | |
79 | 'scope' => 'authx', // Allow general authentication | |
9e99ef4a | 80 | ]); |
b9cbf1a6 TO |
81 | ``` |
82 | ||
83 | This `$token` should be given to some other agent (e.g. web browser, custom script, or email client). | |
84 | ||
85 | For example, here's a custom script which uses the `$token` to call APIv3 (`Contact.get`). The token is based with the common HTTP authorization header: | |
9e99ef4a | 86 | |
b9cbf1a6 | 87 | ```php |
9e99ef4a | 88 | $options = ['http' => [ |
b9cbf1a6 TO |
89 | 'method' => 'GET', |
90 | 'header' => "Authorization: Bearer $token", | |
9e99ef4a | 91 | ]]; |
b9cbf1a6 TO |
92 | $url = 'https://example.org/civicrm/ajax/rest?entity=Contact&action=get&json=' |
93 | . urlencode(json_encode(["id" => "user_contact_id"])); | |
9e99ef4a TO |
94 | $context = stream_context_create($options); |
95 | $response = file_get_contents($url, false, $context); | |
7946d659 TO |
96 | ``` |
97 | ||
b9cbf1a6 | 98 | Alternatively, if you needed to send an email with a sign-in link, the JWT could be passed as an `?_authx` parameter. |
7946d659 | 99 | |
9e99ef4a | 100 | ```php |
b9cbf1a6 TO |
101 | $url = CRM_Utils_System::url('civicrm/dashboard', [ |
102 | '_authx' => "Bearer $token", | |
103 | '_authxSes' => 1, | |
104 | ], TRUE, NULL, FALSE); | |
105 | $html = sprintf('<body>Here is your login link: <a href="%s">%s</a></body>', | |
106 | htmlentities($url), htmlentities($url)); | |
107 | CRM_Utils_Mail::send([...'html' => $html...]); | |
108 | ``` | |
109 | ||
110 | ### Username and Password | |
111 | ||
112 | By default, username/password authentication is not enabled. | |
113 | ||
114 | ``` | |
115 | $ curl 'https://demouser:demopass@example.org/civicrm/authx/id' | |
116 | HTTP 401 Password authentication is not supported | |
117 | ``` | |
118 | ||
119 | However, if you activate it, then it will work with standard HTTP clients: | |
120 | ||
121 | ``` | |
122 | $ cv ev 'Civi::settings()->set("authx_header_cred", ["pass","jwt"]);' | |
123 | ||
124 | $ curl 'https://demouser:demopass@example.org/civicrm/authx/id' | |
125 | {"contact_id":203,"user_id":"2"} | |
7946d659 | 126 | ``` |