@@ -18,7 +18,7 @@ To integrate Swan's Strong Customer Authentication (SCA), you'll need:
18
18
19
19
1 . An in-app browser package.
20
20
1 . A custom URL scheme with a deep link listener package.
21
- 1 . A backend server (this guide uses ` https://awesome -api.com ` ).
21
+ 1 . A backend server (this guide uses ` https://my-company -api.com ` ).
22
22
23
23
Check out [ Swan's demo app GitHub repository] ( https://github.com/swan-io/swan-partner-mobile ) for an integration example.
24
24
@@ -31,11 +31,7 @@ Use the documentation for your system's recommended API to set up your in-app br
31
31
| Android | [ Custom Tabs API] ( https://developer.android.com/jetpack/androidx/releases/browser ) | [ Swan implementation] ( https://github.com/swan-io/react-native-browser/blob/main/android/src/main/java/io/swan/rnbrowser/RNSwanBrowserModuleImpl.java ) |
32
32
| iOS | [ SFSafariViewController API] ( https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller ) | [ Swan implementation] ( https://github.com/swan-io/react-native-browser/blob/main/ios/RNSwanBrowser.mm ) |
33
33
34
- There are also several wrappers available for these APIs:
35
-
36
- - Swan's official package: [ @swan-io/react-native-browser ] ( https://github.com/swan-io/react-native-browser )
37
- - [ @capacitor/browser ] ( https://capacitorjs.com/docs/apis/browser )
38
- - [ flutter_inappwebview ChromeSafariBrowser] ( https://inappwebview.dev/docs/in-app-browsers/chrome-safari-browser )
34
+ We provide an official React Native package: [ @swan-io/react-native-browser ] ( https://github.com/swan-io/react-native-browser )
39
35
40
36
:::warning Follow the references
41
37
When using frameworks other than React Native, ensure that your package is configured in a way that follows Swan's implementation references.
@@ -50,55 +46,27 @@ Use the documentation for your system's recommended tutorial to set up the deep
50
46
| Android | [ Create deep links to app content] ( https://developer.android.com/training/app-links/deep-linking ) |
51
47
| iOS | [ Defining a custom URL scheme for your app] ( https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app ) |
52
48
53
- There are also several guides available to listen for deep link configuration:
54
-
55
- - [ React Native] ( https://reactnative.dev/docs/linking )
56
- - [ Flutter] ( https://docs.flutter.dev/development/ui/navigation/deep-linking )
57
- - [ Capacitor] ( https://capacitorjs.com/docs/apis/app#addlistenerappurlopen- )
49
+ If you are using React Native, follow [ this guide] ( https://reactnative.dev/docs/linking ) .
58
50
59
51
## Step 3: Configure the sign-in process { #configure - signin }
60
52
61
53
This five-step process describes an ideal path for your users to sign in.
62
54
63
- :::info Using Swan's react-native-browser package
64
- Swan's ` react-native-browser ` takes care of listening to the deep link event.
65
- Please ** ignore the ` listener ` declarations** in steps 3.1, 3.5, and 4.3.
66
-
67
- Instead, use the following ** ` onClose ` option** instead:
68
-
69
- ``` js
70
- import { openBrowser } from " @swan-io/react-native-browser" ;
71
-
72
- const onAction = () => {
73
- // not needed with @swan-io/react-native-browser
74
- // const listener = Linking.addListener("url", ({ url }) => {});
75
-
76
- openBrowser (" https://awesome-api.com/endpoint" , {
77
- onClose : (url ) => {
78
- // fired on browser closed
79
- // url will be defined if the browser has been closed via deeplink
80
- },
81
- });
82
- };
83
- ```
84
-
85
- :::
86
-
87
55
### 3.1 Sign-in button { #configure - signin - button }
88
56
89
57
First, the user clicks a sign-in button in your app.
90
58
Clicking this button opens an in-app browser and starts listening for deep links.
91
59
92
60
``` js showLineNumbers
93
- const onSignInWithSwanButtonPress = () => {
94
- const listener = Linking .addListener (" url" , ({ url }) => {
95
- // called when deep link is visited
96
- });
61
+ Linking .addListener (" url" , ({ url }) => {
62
+ // called when deep link is visited
63
+ });
97
64
98
- InAppBrowser .open (" https://awesome-api.com/auth" , {
99
- onClose : () => listener .remove (), // executed when the browser is closed
100
- });
101
- };
65
+ // …
66
+
67
+ openBrowser (" https://my-company-api.com/auth" ).catch ((error ) => {
68
+ console .error (error);
69
+ });
102
70
```
103
71
104
72
### 3.2 Authorization URL { #configure - signin - url }
@@ -110,7 +78,7 @@ app.get("/auth", async (req, reply) => {
110
78
const params = querystring .encode ({
111
79
client_id: " $YOUR_CLIENT_ID" ,
112
80
response_type: " code" ,
113
- redirect_uri: " https://awesome -api.com/auth/callback" , // must be registered in our dashboard
81
+ redirect_uri: " https://my-company -api.com/auth/callback" , // must be registered in our dashboard
114
82
scope: [" openid" , " offline" ].join (" " ),
115
83
state: generateUserState (req),
116
84
});
@@ -121,7 +89,7 @@ app.get("/auth", async (req, reply) => {
121
89
122
90
### 3.3 User logs in { #configure - signin - login }
123
91
124
- The user logs in and is redirected to https://awesome -api.com/auth/callback .
92
+ The user logs in and is redirected to https://my-company -api.com/auth/callback .
125
93
126
94
### 3.4 Server responds { #configure - signin - server }
127
95
@@ -143,15 +111,13 @@ app.get("/auth/callback", async (req, reply) => {
143
111
client_secret: " $YOUR_CLIENT_SECRET" ,
144
112
grant_type: " authorization_code" ,
145
113
code: req .query .code ,
146
- redirect_uri: " https://awesome -api.com/auth/callback" ,
114
+ redirect_uri: " https://my-company -api.com/auth/callback" ,
147
115
});
148
116
149
117
const sessionId = randomUUID ();
150
118
await storeTokenInKeyValueStore (sessionId, token);
151
119
152
- return reply .redirect (
153
- ` com.awesome.app://browser/close?sessionId=${ sessionId} `
154
- );
120
+ return reply .redirect (` com.company.myapp://close?sessionId=${ sessionId} ` );
155
121
});
156
122
```
157
123
@@ -160,25 +126,25 @@ app.get("/auth/callback", async (req, reply) => {
160
126
Finally, the listener closes the in-app browser and stores the identifier.
161
127
162
128
``` js showLineNumbers
163
- const onSignInWithSwanButtonPress = () => {
164
- const listener = Linking .addListener (" url" , ({ url }) => {
165
- // called when deep link is visited
129
+ Linking .addListener (" url" , ({ url }) => {
130
+ // called when deep link is visited
166
131
167
- if (url .startsWith (" com.awesome.app://browser /close" )) {
168
- InAppBrowser . close ();
132
+ if (url .startsWith (" com.company.myapp:/ /close" )) {
133
+ closeBrowser (); // required on iOS
169
134
170
- const { query } = parseUrl (url);
135
+ const { query } = parseUrl (url);
171
136
172
- if (query .sessionId ) {
173
- AsyncStorage .setItem (" sessionId" , query .sessionId );
174
- }
137
+ if (query .sessionId ) {
138
+ AsyncStorage .setItem (" sessionId" , query .sessionId );
175
139
}
176
- });
140
+ }
141
+ });
177
142
178
- InAppBrowser .open (" https://awesome-api.com/auth" , {
179
- onClose : () => listener .remove (), // executed when the browser is closed
180
- });
181
- };
143
+ // …
144
+
145
+ openBrowser (" https://my-company-api.com/auth" ).catch ((error ) => {
146
+ console .error (error);
147
+ });
182
148
```
183
149
184
150
## Step 4: Configure the consent process { #configure - consent }
@@ -197,13 +163,11 @@ First, the user clicks a **Create a card** button in your app.
197
163
Your server API receives the call with the following ` sessionId ` header.
198
164
199
165
``` js showLineNumbers
200
- const onAddCardButtonPress = async () => {
201
- const sessionId = await AsyncStorage .getItem (" sessionId" );
166
+ const sessionId = await AsyncStorage .getItem (" sessionId" );
202
167
203
- await client .post (" https://awesome-api.com/add-card" , {
204
- headers: { sessionId },
205
- });
206
- };
168
+ await client .post (" https://my-company-api.com/add-card" , {
169
+ headers: { sessionId },
170
+ });
207
171
```
208
172
209
173
### 4.2 Consent URL { #configure - consent - url }
@@ -218,7 +182,7 @@ app.get("/add-card", async (req, reply) => {
218
182
{
219
183
input: {
220
184
// …
221
- consentRedirectUrl: " https://awesome -api.com/add-card/callback" ,
185
+ consentRedirectUrl: " https://my-company -api.com/add-card/callback" ,
222
186
},
223
187
},
224
188
{
@@ -237,26 +201,27 @@ app.get("/add-card", async (req, reply) => {
237
201
The app opens an in-app browser and starts listening for deep links.
238
202
239
203
``` js showLineNumbers
240
- const onAddCardButtonPress = async () => {
241
- const sessionId = await AsyncStorage .getItem (" sessionId" );
204
+ Linking .addListener (" url" , ({ url }) => {
205
+ // called when deep link is visited
206
+ });
242
207
243
- const { consentUrl } = await client .post (" https://awesome-api.com/add-card" , {
244
- headers: { sessionId },
245
- });
208
+ // …
246
209
247
- const listener = Linking .addListener (" url" , ({ url }) => {
248
- // called when deep link is visited
249
- });
210
+ const sessionId = await AsyncStorage .getItem (" sessionId" );
250
211
251
- InAppBrowser .open (consentUrl, {
252
- onClose : () => listener .remove (), // executed when the browser is closed
253
- });
254
- };
212
+ const { consentUrl } = await client .post (
213
+ " https://my-company-api.com/add-card" ,
214
+ { headers: { sessionId } }
215
+ );
216
+
217
+ openBrowser (consentUrl).catch ((error ) => {
218
+ console .error (error);
219
+ });
255
220
```
256
221
257
222
### 4.4 User consents { #configure - consent - user }
258
223
259
- The user consents and is redirected to https://awesome -api.com/add-card/callback .
224
+ The user consents and is redirected to https://my-company -api.com/add-card/callback .
260
225
261
226
During redirection, Swan adds these query parameters to the URL.
262
227
@@ -273,7 +238,7 @@ Your server performs any necessary additional operations, then redirects the use
273
238
``` js showLineNumbers
274
239
app .get (" /add-card/callback" , async (req , reply ) => {
275
240
// perform additional operations…
276
- return reply .redirect (" com.awesome.app://browser /close" );
241
+ return reply .redirect (" com.company.myapp:/ /close" );
277
242
});
278
243
```
279
244
@@ -282,23 +247,22 @@ app.get("/add-card/callback", async (req, reply) => {
282
247
Finally, the listener closes the in-app browser.
283
248
284
249
``` js showLineNumbers
285
- const onAddCardButtonPress = async ( ) => {
286
- const sessionId = await AsyncStorage . getItem ( " sessionId " );
250
+ Linking . addListener ( " url " , ({ url } ) => {
251
+ // called when deep link is visited
287
252
288
- const { consentUrl } = await client .post (" https://awesome-api.com/add-card" , {
289
- headers: { sessionId },
290
- });
253
+ if (url .startsWith (" com.company.myapp://close" )) {
254
+ closeBrowser (); // required on iOS
255
+ }
256
+ });
291
257
292
- const listener = Linking .addListener (" url" , ({ url }) => {
293
- // called when deep link is visited
258
+ const sessionId = await AsyncStorage .getItem (" sessionId" );
294
259
295
- if ( url . startsWith ( " com.awesome.app://browser/close " )) {
296
- InAppBrowser . close ();
297
- }
298
- } );
260
+ const { consentUrl } = await client . post (
261
+ " https://my-company-api.com/add-card " ,
262
+ { headers : { sessionId } }
263
+ );
299
264
300
- InAppBrowser .open (consentUrl, {
301
- onClose : () => listener .remove (), // executed when the browser is closed
302
- });
303
- };
265
+ openBrowser (consentUrl).catch ((error ) => {
266
+ console .error (error);
267
+ });
304
268
```
0 commit comments