blob: 196b9f7891144fbd96569cfb8454a00e91f5914d [file] [log] [blame] [view]
Devon Carew4172ae52022-10-25 00:54:311[![Dart CI](https://github.com/dart-lang/oauth2/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/oauth2/actions/workflows/test-package.yml)
2[![pub package](https://img.shields.io/pub/v/oauth2.svg)](https://pub.dev/packages/oauth2)
3[![package publisher](https://img.shields.io/pub/publisher/oauth2.svg)](https://pub.dev/packages/oauth2/publisher)
4
Nate Bosch1f4a79d2020-07-20 21:16:165A client library for authenticating with a remote service via OAuth2 on behalf
6of a user, and making authorized HTTP requests with the user's OAuth2
[email protected]9c0b5542014-05-05 20:25:137credentials.
[email protected]c16d7662014-03-26 22:18:108
Devon Carew4172ae52022-10-25 00:54:319## About OAuth2
10
Nate Bosch1f4a79d2020-07-20 21:16:1611OAuth2 allows a client (the program using this library) to access and manipulate
12a resource that's owned by a resource owner (the end user) and lives on a remote
13server. The client directs the resource owner to an authorization server
14(usually but not always the same as the server that hosts the resource), where
15the resource owner tells the authorization server to give the client an access
16token. This token serves as proof that the client has permission to access
17resources on behalf of the resource owner.
[email protected]c16d7662014-03-26 22:18:1018
19OAuth2 provides several different methods for the client to obtain
20authorization. At the time of writing, this library only supports the
Nate Bosch1f4a79d2020-07-20 21:16:1621[Authorization Code Grant][authorizationCodeGrantSection],
22[Client Credentials Grant][clientCredentialsGrantSection] and
23[Resource Owner Password Grant][resourceOwnerPasswordGrantSection] flows, but
24more may be added in the future.
[email protected]c16d7662014-03-26 22:18:1025
Natalie Weizenbaum1e901282015-09-14 19:55:5626## Authorization Code Grant
Nate Bosch1f4a79d2020-07-20 21:16:1627
28**Resources:** [Class summary][authorizationCodeGrantMethod],
29[OAuth documentation][authorizationCodeGrantDocs]
Natalie Weizenbaum1e901282015-09-14 19:55:5630
[email protected]c16d7662014-03-26 22:18:1031```dart
ovo-64773d802018-01-01 22:06:3032import 'dart:io';
Nate Bosch1f4a79d2020-07-20 21:16:1633
[email protected]c16d7662014-03-26 22:18:1034import 'package:oauth2/oauth2.dart' as oauth2;
35
36// These URLs are endpoints that are provided by the authorization
37// server. They're usually included in the server's documentation of its
38// OAuth2 API.
39final authorizationEndpoint =
Nate Bosch1f4a79d2020-07-20 21:16:1640 Uri.parse('http://example.com/oauth2/authorization');
41final tokenEndpoint = Uri.parse('http://example.com/oauth2/token');
[email protected]c16d7662014-03-26 22:18:1042
43// The authorization server will issue each client a separate client
44// identifier and secret, which allows the server to tell which client
45// is accessing it. Some servers may also have an anonymous
46// identifier/secret pair that any client may use.
47//
48// Note that clients whose source code or binary executable is readily
49// available may not be able to make sure the client secret is kept a
50// secret. This is fine; OAuth2 servers generally won't rely on knowing
51// with certainty that a client is who it claims to be.
Nate Bosch1f4a79d2020-07-20 21:16:1652final identifier = 'my client identifier';
53final secret = 'my client secret';
[email protected]c16d7662014-03-26 22:18:1054
55// This is a URL on your application's server. The authorization server
56// will redirect the resource owner here once they've authorized the
57// client. The redirection will include the authorization code in the
58// query parameters.
Nate Bosch1f4a79d2020-07-20 21:16:1659final redirectUrl = Uri.parse('http://my-site.com/oauth2-redirect');
[email protected]c16d7662014-03-26 22:18:1060
Natalie Weizenbaum05898892015-08-24 23:58:4361/// A file in which the users credentials are stored persistently. If the server
62/// issues a refresh token allowing the client to refresh outdated credentials,
63/// these may be valid indefinitely, meaning the user never has to
64/// re-authenticate.
Nate Bosch1f4a79d2020-07-20 21:16:1665final credentialsFile = File('~/.myapp/credentials.json');
Natalie Weizenbaum05898892015-08-24 23:58:4366
67/// Either load an OAuth2 client from saved credentials or authenticate a new
68/// one.
Nate Bosch1f4a79d2020-07-20 21:16:1669Future<oauth2.Client> createClient() async {
Natalie Weizenbaum05898892015-08-24 23:58:4370 var exists = await credentialsFile.exists();
71
72 // If the OAuth2 credentials have already been saved from a previous run, we
73 // just want to reload them.
[email protected]c16d7662014-03-26 22:18:1074 if (exists) {
Nate Bosch1f4a79d2020-07-20 21:16:1675 var credentials =
76 oauth2.Credentials.fromJson(await credentialsFile.readAsString());
77 return oauth2.Client(credentials, identifier: identifier, secret: secret);
[email protected]c16d7662014-03-26 22:18:1078 }
79
Natalie Weizenbaum05898892015-08-24 23:58:4380 // If we don't have OAuth2 credentials yet, we need to get the resource owner
81 // to authorize us. We're assuming here that we're a command-line application.
Nate Bosch1f4a79d2020-07-20 21:16:1682 var grant = oauth2.AuthorizationCodeGrant(
Natalie Weizenbaum45f36282015-08-26 20:47:1883 identifier, authorizationEndpoint, tokenEndpoint,
84 secret: secret);
[email protected]c16d7662014-03-26 22:18:1085
Levi Hassel6649c872020-04-27 09:12:2686 // A URL on the authorization server (authorizationEndpoint with some additional
87 // query parameters). Scopes and state can optionally be passed into this method.
88 var authorizationUrl = grant.getAuthorizationUrl(redirectUrl);
89
90 // Redirect the resource owner to the authorization URL. Once the resource
91 // owner has authorized, they'll be redirected to `redirectUrl` with an
Nate Bosch1f4a79d2020-07-20 21:16:1692 // authorization code. The `redirect` should cause the browser to redirect to
93 // another URL which should also have a listener.
[email protected]c16d7662014-03-26 22:18:1094 //
Nate Bosch1f4a79d2020-07-20 21:16:1695 // `redirect` and `listen` are not shown implemented here. See below for the
96 // details.
Levi Hassel6649c872020-04-27 09:12:2697 await redirect(authorizationUrl);
Levi Hassel6649c872020-04-27 09:12:2698 var responseUrl = await listen(redirectUrl);
Natalie Weizenbaum05898892015-08-24 23:58:4399
100 // Once the user is redirected to `redirectUrl`, pass the query parameters to
101 // the AuthorizationCodeGrant. It will validate them and extract the
102 // authorization code to create a new Client.
Levi Hassel6649c872020-04-27 09:12:26103 return await grant.handleAuthorizationResponse(responseUrl.queryParameters);
Natalie Weizenbaum05898892015-08-24 23:58:43104}
105
Nate Bosch1f4a79d2020-07-20 21:16:16106void main() async {
107 var client = await createClient();
Natalie Weizenbaum05898892015-08-24 23:58:43108
109 // Once you have a Client, you can use it just like any other HTTP client.
Nate Bosch1f4a79d2020-07-20 21:16:16110 print(await client.read('http://example.com/protected-resources.txt'));
Natalie Weizenbaum05898892015-08-24 23:58:43111
112 // Once we're done with the client, save the credentials file. This ensures
113 // that if the credentials were automatically refreshed while using the
114 // client, the new credentials are available for the next run of the
115 // program.
116 await credentialsFile.writeAsString(client.credentials.toJson());
Natalie Weizenbaum05898892015-08-24 23:58:43117}
[email protected]c16d7662014-03-26 22:18:10118```
Erik Grimesc6585072015-09-14 19:55:18119
Levi Hassel6649c872020-04-27 09:12:26120<details>
Nate Bosch1f4a79d2020-07-20 21:16:16121 <summary>Click here to learn how to implement `redirect` and `listen`.</summary>
Levi Hassel6649c872020-04-27 09:12:26122
Nate Bosch1f4a79d2020-07-20 21:16:16123--------------------------------------------------------------------------------
124
125There is not a universal example for implementing `redirect` and `listen`,
126because different options exist for each platform.
127
128For Flutter apps, there's two popular approaches:
129
1301. Launch a browser using [url_launcher][] and listen for a redirect using
131 [uni_links][].
132
133 ```dart
134 if (await canLaunch(authorizationUrl.toString())) {
135 await launch(authorizationUrl.toString()); }
136
137 // ------- 8< -------
138
139 final linksStream = getLinksStream().listen((Uri uri) async {
140 if (uri.toString().startsWith(redirectUrl)) {
141 responseUrl = uri;
142 }
143 });
144 ```
145
1461. Launch a WebView inside the app and listen for a redirect using
147 [webview_flutter][].
148
149 ```dart
150 WebView(
151 javascriptMode: JavascriptMode.unrestricted,
152 initialUrl: authorizationUrl.toString(),
153 navigationDelegate: (navReq) {
154 if (navReq.url.startsWith(redirectUrl)) {
155 responseUrl = Uri.parse(navReq.url);
156 return NavigationDecision.prevent;
Levi Hassel6649c872020-04-27 09:12:26157 }
Nate Bosch1f4a79d2020-07-20 21:16:16158 return NavigationDecision.navigate;
159 },
160 // ------- 8< -------
161 );
162 ```
Levi Hassel6649c872020-04-27 09:12:26163
Nate Bosch1f4a79d2020-07-20 21:16:16164For Dart apps, the best approach depends on the available options for accessing
165a browser. In general, you'll need to launch the authorization URL through the
166client's browser and listen for the redirect URL.
Levi Hassel6649c872020-04-27 09:12:26167</details>
168
Tobe Osakwe46d0f742019-11-19 22:43:13169## Client Credentials Grant
Nate Bosch1f4a79d2020-07-20 21:16:16170
171**Resources:** [Method summary][clientCredentialsGrantMethod],
172[OAuth documentation][clientCredentialsGrantDocs]
Levi Hassel6649c872020-04-27 09:12:26173
Tobe Osakwe46d0f742019-11-19 22:43:13174```dart
175// This URL is an endpoint that's provided by the authorization server. It's
176// usually included in the server's documentation of its OAuth2 API.
177final authorizationEndpoint =
Nate Bosch1f4a79d2020-07-20 21:16:16178 Uri.parse('http://example.com/oauth2/authorization');
Tobe Osakwe46d0f742019-11-19 22:43:13179
180// The OAuth2 specification expects a client's identifier and secret
181// to be sent when using the client credentials grant.
182//
183// Because the client credentials grant is not inherently associated with a user,
184// it is up to the server in question whether the returned token allows limited
185// API access.
186//
187// Either way, you must provide both a client identifier and a client secret:
Nate Bosch1f4a79d2020-07-20 21:16:16188final identifier = 'my client identifier';
189final secret = 'my client secret';
Tobe Osakwe46d0f742019-11-19 22:43:13190
191// Calling the top-level `clientCredentialsGrant` function will return a
192// [Client] instead.
193var client = await oauth2.clientCredentialsGrant(
194 authorizationEndpoint, identifier, secret);
195
196// With an authenticated client, you can make requests, and the `Bearer` token
197// returned by the server during the client credentials grant will be attached
198// to any request you make.
Nate Bosch1f4a79d2020-07-20 21:16:16199var response =
200 await client.read('https://example.com/api/some_resource.json');
Tobe Osakwe46d0f742019-11-19 22:43:13201
202// You can save the client's credentials, which consists of an access token, and
203// potentially a refresh token and expiry date, to a file. This way, subsequent runs
204// do not need to reauthenticate, and you can avoid saving the client identifier and
205// secret.
206await credentialsFile.writeAsString(client.credentials.toJson());
207```
208
Natalie Weizenbaum1e901282015-09-14 19:55:56209## Resource Owner Password Grant
Nate Bosch1f4a79d2020-07-20 21:16:16210
211**Resources:** [Method summary][resourceOwnerPasswordGrantMethod],
212[OAuth documentation][resourceOwnerPasswordGrantDocs]
Erik Grimesc6585072015-09-14 19:55:18213
Natalie Weizenbaum1e901282015-09-14 19:55:56214```dart
215// This URL is an endpoint that's provided by the authorization server. It's
216// usually included in the server's documentation of its OAuth2 API.
217final authorizationEndpoint =
Nate Bosch1f4a79d2020-07-20 21:16:16218 Uri.parse('http://example.com/oauth2/authorization');
Natalie Weizenbaum1e901282015-09-14 19:55:56219
220// The user should supply their own username and password.
Nate Bosch1f4a79d2020-07-20 21:16:16221final username = 'example user';
222final password = 'example password';
Natalie Weizenbaum1e901282015-09-14 19:55:56223
224// The authorization server may issue each client a separate client
225// identifier and secret, which allows the server to tell which client
226// is accessing it. Some servers may also have an anonymous
227// identifier/secret pair that any client may use.
228//
229// Some servers don't require the client to authenticate itself, in which case
230// these should be omitted.
Nate Bosch1f4a79d2020-07-20 21:16:16231final identifier = 'my client identifier';
232final secret = 'my client secret';
Natalie Weizenbaum1e901282015-09-14 19:55:56233
234// Make a request to the authorization endpoint that will produce the fully
235// authenticated Client.
236var client = await oauth2.resourceOwnerPasswordGrant(
237 authorizationEndpoint, username, password,
238 identifier: identifier, secret: secret);
239
240// Once you have the client, you can use it just like any other HTTP client.
Nate Bosch1f4a79d2020-07-20 21:16:16241var result = await client.read('http://example.com/protected-resources.txt');
Natalie Weizenbaum1e901282015-09-14 19:55:56242
243// Once we're done with the client, save the credentials file. This will allow
244// us to re-use the credentials and avoid storing the username and password
245// directly.
Nate Bosch1f4a79d2020-07-20 21:16:16246File('~/.myapp/credentials.json').writeAsString(client.credentials.toJson());
Erik Grimesc6585072015-09-14 19:55:18247```
Levi Hassel6649c872020-04-27 09:12:26248
249[authorizationCodeGrantDocs]: https://oauth.net/2/grant-types/authorization-code/
250[authorizationCodeGrantMethod]: https://pub.dev/documentation/oauth2/latest/oauth2/AuthorizationCodeGrant-class.html
251[authorizationCodeGrantSection]: #authorization-code-grant
252[clientCredentialsGrantDocs]: https://oauth.net/2/grant-types/client-credentials/
253[clientCredentialsGrantMethod]: https://pub.dev/documentation/oauth2/latest/oauth2/clientCredentialsGrant.html
254[clientCredentialsGrantSection]: #client-credentials-grant
255[resourceOwnerPasswordGrantDocs]: https://oauth.net/2/grant-types/password/
256[resourceOwnerPasswordGrantMethod]: https://pub.dev/documentation/oauth2/latest/oauth2/resourceOwnerPasswordGrant.html
257[resourceOwnerPasswordGrantSection]: #resource-owner-password-grant
258[uni_links]: https://pub.dev/packages/uni_links
259[url_launcher]: https://pub.dev/packages/url_launcher
260[webview_flutter]: https://pub.dev/packages/webview_flutter