|
2 | 2 | title: Castle Destination
|
3 | 3 | id: 56a8f566e954a874ca44d3b0
|
4 | 4 | ---
|
5 |
| -Once you enable the Castle integration, the [Castle JavaScript snippet](https://docs.castle.io/docs/sdk-browser) is placed on your website, and user data starts appearing in the Castle dashboard. |
6 |
| -Client-side tracking works out of the box, however **your existing server-side calls need to be extended** with data from the incoming request. |
| 5 | +[Castle](https://castle.io/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners){:target="_blank"} monitors every step of the customer journey to help visualize and proactively block fraud that would otherwise fly under the radar. Types of fraud or abuse that can be managed include bots, fake accounts, multi-accounting, and account sharing. |
7 | 6 |
|
8 |
| -Castle supports calling `identify`, `page`, `screen`, and `group`. Castle does *not* support the `alias` call. |
| 7 | +Castle maintains this destination. For any issues with the destination, contact the [Castle support team](mailto:[email protected]). |
9 | 8 |
|
10 |
| -## Integration steps |
11 |
| -1. Track successful and failed logins |
12 |
| -1. Extend server-side tracking with request properties |
13 |
| -1. `identify`, preferably on the server-side |
14 |
| -1. _Optional:_ Use Castle's `authenticate` API to request a risk score |
15 |
| -1. _Recommended:_ Secure Mode |
| 9 | +## Getting Started |
16 | 10 |
|
17 |
| -## Tracking successful and failed logins |
18 |
| -A baseline integration of Castle includes tracking [successful and failed login attempts](https://docs.castle.io/docs/failed-logins). If you track these events using a Segment integration, you can use [Event Mapping](https://dashboard.castle.io/settings/events) to indicate which events correspond to Castle reserved events. |
| 11 | +1. Navigate to **Connections > Catalog** in the Segment web app. |
| 12 | +2. Search for *Castle* in the **Destinations** tab of the catalog, and select it, and click **Configure Castle**. |
| 13 | +3. Choose the sources you want to connect the destination to. |
| 14 | +3. Enter the "Publishable Key" the Publishable Key field. Find the Publishable Key on the Castle dashboard. |
| 15 | +Calls are now visible in Castle dashboards in real-time. |
19 | 16 |
|
20 | 17 | > info ""
|
21 |
| -> If you request a Castle risk score for the "Logged in" event, you should **not** map that event to Castle's reserved `$login.succeeded`. Instead, [`authenticate`](https://docs.castle.io/docs/authentication-method) that event through Castle. See next section on _Requesting a risk score_. |
| 18 | +> Castle ingests Segment Client-side events. Server-side events are dropped and not processed. |
| 19 | +> Castle only supports web integrations through Segment, but is in the process of working on mobile support. |
22 | 20 |
|
23 |
| -Here are two Ruby examples on how to track successful and failed login attempts (`context` and `integration` have been omitted for brevity): |
24 | 21 |
|
25 |
| -```ruby |
26 |
| -analytics.track( |
27 |
| - user_id: '019mr8mf4r', |
28 |
| - event: 'Logged in' |
29 |
| -) |
30 |
| -``` |
31 |
| - |
32 |
| -When you track failed logins, you can protect against account threats such as password guessing. If you don't know which user that generated the failed login, omit the `user_id`. Instead, whenever you have access to the user-submitted email field, add this to the event properties as `email` or `username` depending on how you identify your users. Sending both `user_id` and `email` at the same time does not cause any data problems. |
33 |
| - |
34 |
| -```ruby |
35 |
| -# known user |
36 |
| -analytics.track( |
37 |
| - user_id: '019mr8mf4r', |
38 |
| - event: 'Failed to log in' |
39 |
| -) |
40 |
| -# unknown user |
41 |
| -analytics.track( |
42 |
| - anonymous_id: UUID.generate, |
43 |
| - event: 'Failed to log in', |
44 |
| - properties: { |
45 |
| - |
46 |
| - } |
47 |
| -) |
48 |
| -``` |
49 |
| - |
50 |
| -> info "" |
51 |
| -> Segment requires either `user_id` or `anonymous_id` for the request to be processed. If you don't know which user generated the failed login create a UUID and provide it as `anonymous_id` |
52 |
| -
|
53 |
| -## Extending server-side tracking with request properties |
54 |
| -Tracking events from your server-side is crucial to prevent requests from getting blocked by malicious actors. This is recommended for all [Castle's reserved events](https://docs.castle.io/docs/events), such as logins and password changes. |
55 | 22 |
|
56 |
| -> warning "" |
57 |
| -> Server-side `track` events are dropped by Castle unless they contain the properties listed below. `identify` calls still create or update a user, but don't create a device if these properties are missing: |
58 |
| -> - `context.ip`. The user's IP address, i.e. not your server's internal IP |
59 |
| -> - `context.user_agent`, alternatively `context.headers` containing at least the `user_agent` field. |
60 |
| -> - `context.client_id`. The _Client ID_ forwarded by the web or mobile SDK. |
61 | 23 |
|
62 |
| -These properties are described in detail in the next section. |
63 | 24 |
|
64 |
| -If you aren't tracking the properties above, you can still make the event appear in the user timeline by configuring it to _Force Track_ in the [Castle dashboard](https://dashboard.castle.io/settings/events). However, it does not attach to a device or contribute to the risk score. |
| 25 | +## Page |
65 | 26 |
|
66 |
| -Here's a Ruby example of a server-side `track` call extended with request properties: |
| 27 | +If you're not familiar with the Segment Specs, take a look to understand what the [Page call](/docs/connections/spec/page/) does. An example call looks like: |
67 | 28 |
|
68 |
| -```ruby |
69 |
| -analytics.track( |
70 |
| - user_id: '019mr8mf4r', |
71 |
| - event: 'Logged in' |
72 |
| - context: { |
73 |
| - ip: '8.8.8.8', |
74 |
| - user_agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36', |
75 |
| - client_id: '7a31b5a1-7e01-4377-b086-5a488ec8a0ca', |
76 |
| - headers: { |
77 |
| - accept_language: 'da, en-gb;q=0.8, en;q=0.7', |
78 |
| - ... |
79 |
| - } |
80 |
| - }) |
| 29 | +``` |
| 30 | +analytics.page() |
81 | 31 | ```
|
82 | 32 |
|
83 |
| -> **Note:** If you're concerned about sending `client_id` and `headers` to all of your active Segment integrations, instead include them in the `integrations.Castle` object to keep them private to your Castle integration. |
84 |
| -
|
85 |
| -### The `client_id` property |
86 |
| -By forwarding a client identifier from the client-side to the server-side, you can link activity from the two sources to form a strong protection against attacks where this link is not present. |
87 | 33 |
|
88 |
| -The Castle JavaScript SDK (loaded by Analytics.js) forwards the client identifier as a browser cookie named `__cid`. |
89 | 34 |
|
90 |
| -The Castle [iOS](https://docs.castle.io/docs/sdk-ios) and [Android](https://docs.castle.io/docs/sdk-android) SDKs forward it as the HTTP header `X-Castle-Client-Id`. See the respective documentation pages for instructions on how to configure the header forwarding. |
| 35 | +Segment sends Page calls to Castle as `$page` events. |
91 | 36 |
|
92 |
| -Here's a Ruby example on how to extract the Client ID on your server-side: |
93 | 37 |
|
94 |
| -```ruby |
95 |
| -client_id = |
96 |
| - request.cookies['__cid'] || |
97 |
| - request.headers['X-Castle-Client-Id'] |
98 |
| -``` |
| 38 | +## Track |
99 | 39 |
|
100 |
| -On **iOS**, forward the device UUID as client identifier: |
| 40 | +If you're not familiar with the Segment Specs, take a look to understand what the [Track method](/docs/connections/spec/track/) does. An example call looks like: |
101 | 41 |
|
102 |
| -```objc |
103 |
| -[request setValue:uuid forHTTPHeaderField:@"X-Castle-Client-Id"]; |
104 |
| -NSURL *url = [NSURL URLWithString:@"https://api.yoursite.com/login"]; |
105 |
| -NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; |
106 |
| -NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString; |
| 42 | +``` |
| 43 | +analytics.track('Added to Cart') |
107 | 44 | ```
|
108 | 45 |
|
109 |
| -On **Android**, forward the device identifier from Segment's `Utils` package as client identifier: |
110 | 46 |
|
111 |
| -```java |
112 |
| -// com.segment.analytics.internal.Utils |
113 |
| -String uuid = Utils.getDeviceId(); |
114 |
| -OkHttpClient client = new OkHttpClient(); |
115 |
| -Request request = new Request.Builder() |
116 |
| - .url("https://api.yoursite.com/login") |
117 |
| - .header("X-Castle-Client-Id", uuid) |
118 |
| - .build(); |
119 |
| -``` |
120 | 47 |
|
121 |
| -> **Note**: If you have a client-less integration, for instance if you're using Castle to protect a customer-facing API, set `client_id` to `false`. |
| 48 | +Segment sends Track calls to Castle as a `$custom` events. |
122 | 49 |
|
123 |
| -### The `headers` property |
124 |
| -By forwarding HTTP request headers from the server-side, Castle is able to build a richer device fingerprint and prevent malicious actors from spoofing the client environment. |
125 |
| -For privacy reasons, **you do not want to send the "Cookie" header to Castle**, so make sure you delete if from the list of headers. |
| 50 | +*** |
126 | 51 |
|
127 |
| -``` |
128 |
| -{ |
129 |
| - user_agent: 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)', |
130 |
| - accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', |
131 |
| - accept_language: 'en-us,en;q=0.5', |
132 |
| - accept_encoding: 'gzip,deflate', |
133 |
| - accept_charset: 'ISO-8859-1,utf-8;q=0.7,*;q=0.7' |
134 |
| -} |
135 |
| -``` |
136 | 52 |
|
137 |
| -There are example implementations on how to extract request headers in [PHP](https://github.com/castle/castle-php/blob/e93de1532ef28af17b8bf2bef350e6995a580085/lib/Castle/Request.php#L31), [Ruby](https://github.com/castle/castle-ruby), and [Java](https://github.com/castle/castle-java/blob/96cdc7469aa0995a836100c3dfd370b10f299e8c/src/main/java/io/castle/client/objects/UserInfoHeader.java#L148). |
138 | 53 |
|
139 |
| -## Identify |
140 |
| -When you call [`identify`](/docs/connections/spec/identify), a user will be created in Castle. The Segment special traits `email`, `username`, `name`, `createdAt`, `phone`, and `address` are mapped to Castle's reserved user traits. |
| 54 | +## Secure Mode |
141 | 55 |
|
142 |
| -Any additional traits will be stored on the Castle user model as _custom traits_. |
| 56 | +Send user information as a signed JWT when you use Castle in production. This prevents bad actors from spoofing any user information. |
143 | 57 |
|
144 |
| -> **Recommended:** Prevent `identify` from getting blocked in the client during an account takeover by calling `identify` from your server. |
| 58 | +In your backend code, encode the user as a JWT and sign it using your Castle "API Secret". When Castle receives the JWT, the integrity of the user data is verified to ensure that the data wasn't tampered with. |
145 | 59 |
|
146 |
| -Here's a complete JavaScript example of an `identify` call: |
| 60 | +Below is an example of how to generate a JWT on your backend using the Ruby language: |
147 | 61 |
|
148 |
| -```javascript |
149 |
| -analytics.identify('1234', { |
150 |
| - email : '[email protected]', // recommended |
151 |
| - createdAt: '2015-02-23T22:28:55.387Z', // recommended |
152 |
| - name: 'Johan Brissmyr', // for display |
153 |
| - username: 'brissmyr', // for display |
154 |
| - balance: 1350, // custom trait |
155 |
| - phone: '+1 415 254 9225', // improved risk scoring |
156 |
| - address: { // improved risk scoring |
157 |
| - street: '60 Rausch St', |
158 |
| - city: 'San Francisco', |
159 |
| - state: 'CA', |
160 |
| - postalCode: '94103', |
161 |
| - country: 'USA' |
162 |
| - } |
163 |
| -}); |
| 62 | +```ruby |
| 63 | +jwt_from_backend = JWT.encode({ |
| 64 | + id: '97980cfea0067', |
| 65 | + |
| 66 | +}, ENV.fetch('CASTLE_API_SECRET'), 'HS256') |
164 | 67 | ```
|
165 | 68 |
|
166 |
| -> **Note:** If you call `authenticate` to obtain a risk score, you do *not* need to call `identify` from the server-side. Instead, `authenticate` provides a way to attach `traits` in the same call. |
167 | 69 |
|
168 |
| -## Secure Mode |
169 |
| -Enable Secure Mode to prevent fraudsters from impersonating your users. |
170 |
| - |
171 |
| -> **Note:** Secure Mode is highly encouraged for production deployments, but can wait until after a completed proof a concept. |
172 |
| -To enable Secure Mode in Analytics.js, you pass in the `secure` variable by rendering it in your server-side templates. The `secure` field should be a SHA256 hash of your Castle API Secret and the user ID. |
173 | 70 |
|
174 |
| -Here's an JavaScript example of an `identify` call with Secure Mode being rendered with Ruby server-side templating language: |
| 71 | +Transfer the `user_jwt` object to your frontend through a separate API call, or by injecting the code using a templating language: |
175 | 72 |
|
176 | 73 | ```javascript
|
177 |
| -analytics.identify('1234', { |
178 |
| - |
179 |
| - createdAt: '2015-02-23T22:28:55.387Z', |
| 74 | +var userJwt = "<%= jwt_from_backend %>"; |
| 75 | + |
| 76 | +// Then use the `userJwt` argument instead of `user` when using any of the tracking methods |
| 77 | +Castle.page({userJwt: userJwt}); |
| 78 | + |
| 79 | +analytics.identify('97980cfea0067', { |
| 80 | + |
180 | 81 | }, {
|
181 |
| - integrations: { |
182 |
| - Castle: { |
183 |
| - secure: '<%%= OpenSSL::HMAC.hexdigest("sha256", "YOUR_CASTLE_API_SECRET", current_user.id.to_s) %>' |
184 |
| - } |
| 82 | + Castle: { |
| 83 | + userJwt: userJwt |
185 | 84 | }
|
186 | 85 | });
|
187 | 86 | ```
|
188 | 87 |
|
189 |
| -To use secure mode in your mobile app, you will need to first fetch the secure token from your server-side, for example: |
190 |
| - |
191 |
| -```ruby |
192 |
| -# GET https://api.yoursite.com/token |
193 |
| -def user_token(user_id) |
194 |
| - OpenSSL::HMAC.hexdigest("sha256", "YOUR_CASTLE_API_SECRET", user_id.to_s) |
195 |
| -end |
196 |
| -``` |
197 |
| - |
198 |
| -## Requesting a risk score |
199 |
| -Castle's adaptive authentication tells you whether to allow access, initiate a second factor of authentication, or log out the user. |
200 | 88 |
|
201 |
| -Since all Segment calls are called asynchronously, you'll need to use Castle's native SDKs to perform [adaptive authentication](https://docs.castle.io/docs/authentication-method). |
| 89 | +When Castle receives a JWT version of the user object, its contents override the user object sent the standard Segment way. |
0 commit comments