Improve README style (#80)
- Reflow to a more consistent 80 columns.
- Update Dart code for style - use single quotes, format, drop `new`.
- Rename a function to avoid leading "get".
- Update phrasing about "imaginary" functions - make it clear that these
are omitted implementation details.
- Copy the code from the main block into `example/main.dart` to make it
easier to work with and improve the pub score.
- Remove unused `author` field from pubspec.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21b211c..28cc6e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+# 1.6.2-dev
+
# 1.6.1
* Added fix to make sure that credentials are only refreshed once when multiple calls are made.
diff --git a/README.md b/README.md
index 98df16a..a536740 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,38 @@
-A client library for authenticating with a remote service via OAuth2 on
-behalf of a user, and making authorized HTTP requests with the user's OAuth2
+A client library for authenticating with a remote service via OAuth2 on behalf
+of a user, and making authorized HTTP requests with the user's OAuth2
credentials.
-OAuth2 allows a client (the program using this library) to access and
-manipulate a resource that's owned by a resource owner (the end user) and
-lives on a remote server. The client directs the resource owner to an
-authorization server (usually but not always the same as the server that
-hosts the resource), where the resource owner tells the authorization server
-to give the client an access token. This token serves as proof that the
-client has permission to access resources on behalf of the resource owner.
+OAuth2 allows a client (the program using this library) to access and manipulate
+a resource that's owned by a resource owner (the end user) and lives on a remote
+server. The client directs the resource owner to an authorization server
+(usually but not always the same as the server that hosts the resource), where
+the resource owner tells the authorization server to give the client an access
+token. This token serves as proof that the client has permission to access
+resources on behalf of the resource owner.
OAuth2 provides several different methods for the client to obtain
authorization. At the time of writing, this library only supports the
-[Authorization Code Grant][authorizationCodeGrantSection], [Client Credentials Grant][clientCredentialsGrantSection] and [Resource Owner Password Grant][resourceOwnerPasswordGrantSection] flows, but more may be added in the future.
+[Authorization Code Grant][authorizationCodeGrantSection],
+[Client Credentials Grant][clientCredentialsGrantSection] and
+[Resource Owner Password Grant][resourceOwnerPasswordGrantSection] flows, but
+more may be added in the future.
## Authorization Code Grant
-**Resources:** [Class summary][authorizationCodeGrantMethod], [OAuth documentation][authorizationCodeGrantDocs]
+
+**Resources:** [Class summary][authorizationCodeGrantMethod],
+[OAuth documentation][authorizationCodeGrantDocs]
```dart
import 'dart:io';
+
import 'package:oauth2/oauth2.dart' as oauth2;
// These URLs are endpoints that are provided by the authorization
// server. They're usually included in the server's documentation of its
// OAuth2 API.
final authorizationEndpoint =
- Uri.parse("http://example.com/oauth2/authorization");
-final tokenEndpoint =
- Uri.parse("http://example.com/oauth2/token");
+ Uri.parse('http://example.com/oauth2/authorization');
+final tokenEndpoint = Uri.parse('http://example.com/oauth2/token');
// The authorization server will issue each client a separate client
// identifier and secret, which allows the server to tell which client
@@ -38,38 +43,37 @@
// available may not be able to make sure the client secret is kept a
// secret. This is fine; OAuth2 servers generally won't rely on knowing
// with certainty that a client is who it claims to be.
-final identifier = "my client identifier";
-final secret = "my client secret";
+final identifier = 'my client identifier';
+final secret = 'my client secret';
// This is a URL on your application's server. The authorization server
// will redirect the resource owner here once they've authorized the
// client. The redirection will include the authorization code in the
// query parameters.
-final redirectUrl = Uri.parse("http://my-site.com/oauth2-redirect");
+final redirectUrl = Uri.parse('http://my-site.com/oauth2-redirect');
/// A file in which the users credentials are stored persistently. If the server
/// issues a refresh token allowing the client to refresh outdated credentials,
/// these may be valid indefinitely, meaning the user never has to
/// re-authenticate.
-final credentialsFile = new File("~/.myapp/credentials.json");
+final credentialsFile = File('~/.myapp/credentials.json');
/// Either load an OAuth2 client from saved credentials or authenticate a new
/// one.
-Future<oauth2.Client> getClient() async {
+Future<oauth2.Client> createClient() async {
var exists = await credentialsFile.exists();
// If the OAuth2 credentials have already been saved from a previous run, we
// just want to reload them.
if (exists) {
- var credentials = new oauth2.Credentials.fromJson(
- await credentialsFile.readAsString());
- return new oauth2.Client(credentials,
- identifier: identifier, secret: secret);
+ var credentials =
+ oauth2.Credentials.fromJson(await credentialsFile.readAsString());
+ return oauth2.Client(credentials, identifier: identifier, secret: secret);
}
// If we don't have OAuth2 credentials yet, we need to get the resource owner
// to authorize us. We're assuming here that we're a command-line application.
- var grant = new oauth2.AuthorizationCodeGrant(
+ var grant = oauth2.AuthorizationCodeGrant(
identifier, authorizationEndpoint, tokenEndpoint,
secret: secret);
@@ -79,13 +83,12 @@
// Redirect the resource owner to the authorization URL. Once the resource
// owner has authorized, they'll be redirected to `redirectUrl` with an
- // authorization code.
+ // authorization code. The `redirect` should cause the browser to redirect to
+ // another URL which should also have a listener.
//
- // `redirect` is an imaginary function that redirects the resource
- // owner's browser.
+ // `redirect` and `listen` are not shown implemented here. See below for the
+ // details.
await redirect(authorizationUrl);
-
- // Another imaginary function that listens for a request to `redirectUrl`.
var responseUrl = await listen(redirectUrl);
// Once the user is redirected to `redirectUrl`, pass the query parameters to
@@ -94,73 +97,79 @@
return await grant.handleAuthorizationResponse(responseUrl.queryParameters);
}
-main() async {
- var client = await getClient();
+void main() async {
+ var client = await createClient();
// Once you have a Client, you can use it just like any other HTTP client.
- var result = client.read("http://example.com/protected-resources.txt");
+ print(await client.read('http://example.com/protected-resources.txt'));
// Once we're done with the client, save the credentials file. This ensures
// that if the credentials were automatically refreshed while using the
// client, the new credentials are available for the next run of the
// program.
await credentialsFile.writeAsString(client.credentials.toJson());
-
- print(result);
}
```
<details>
- <summary>Click here to learn how to implement the imaginary functions mentioned above.</summary>
-
- -----
-
- Unfortunately, there's not a universal example for implementing the imaginary functions, `redirect` and `listen`, because different options exist for each platform.
-
- For Flutter apps, there's two popular approaches:
- 1. Launch a browser using [url_launcher][] and listen for a redirect using [uni_links][].
- ```dart
- if (await canLaunch(authorizationUrl.toString())) {
- await launch(authorizationUrl.toString());
- }
+ <summary>Click here to learn how to implement `redirect` and `listen`.</summary>
- ...
-
- final linksStream = getLinksStream().listen((Uri uri) async {
- if (uri.toString().startsWith(redirectUrl)) {
- responseUrl = uri;
+--------------------------------------------------------------------------------
+
+There is not a universal example for implementing `redirect` and `listen`,
+because different options exist for each platform.
+
+For Flutter apps, there's two popular approaches:
+
+1. Launch a browser using [url_launcher][] and listen for a redirect using
+ [uni_links][].
+
+ ```dart
+ if (await canLaunch(authorizationUrl.toString())) {
+ await launch(authorizationUrl.toString()); }
+
+ // ------- 8< -------
+
+ final linksStream = getLinksStream().listen((Uri uri) async {
+ if (uri.toString().startsWith(redirectUrl)) {
+ responseUrl = uri;
+ }
+ });
+ ```
+
+1. Launch a WebView inside the app and listen for a redirect using
+ [webview_flutter][].
+
+ ```dart
+ WebView(
+ javascriptMode: JavascriptMode.unrestricted,
+ initialUrl: authorizationUrl.toString(),
+ navigationDelegate: (navReq) {
+ if (navReq.url.startsWith(redirectUrl)) {
+ responseUrl = Uri.parse(navReq.url);
+ return NavigationDecision.prevent;
}
- });
- ```
+ return NavigationDecision.navigate;
+ },
+ // ------- 8< -------
+ );
+ ```
- 2. Launch a WebView inside the app and listen for a redirect using [webview_flutter][].
- ```dart
- WebView(
- javascriptMode: JavascriptMode.unrestricted,
- initialUrl: authorizationUrl.toString(),
- navigationDelegate: (navReq) {
- if (navReq.url.startsWith(redirectUrl)) {
- responseUrl = Uri.parse(navReq.url);
- return NavigationDecision.prevent;
- }
-
- return NavigationDecision.navigate;
- },
- ...
- );
- ```
-
- For Dart apps, the best approach depends on the available options for accessing a browser. In general, you'll need to launch the authorization URL through the client's browser and listen for the redirect URL.
+For Dart apps, the best approach depends on the available options for accessing
+a browser. In general, you'll need to launch the authorization URL through the
+client's browser and listen for the redirect URL.
</details>
## Client Credentials Grant
-**Resources:** [Method summary][clientCredentialsGrantMethod], [OAuth documentation][clientCredentialsGrantDocs]
+
+**Resources:** [Method summary][clientCredentialsGrantMethod],
+[OAuth documentation][clientCredentialsGrantDocs]
```dart
// This URL is an endpoint that's provided by the authorization server. It's
// usually included in the server's documentation of its OAuth2 API.
final authorizationEndpoint =
- Uri.parse("http://example.com/oauth2/authorization");
+ Uri.parse('http://example.com/oauth2/authorization');
// The OAuth2 specification expects a client's identifier and secret
// to be sent when using the client credentials grant.
@@ -170,8 +179,8 @@
// API access.
//
// Either way, you must provide both a client identifier and a client secret:
-final identifier = "my client identifier";
-final secret = "my client secret";
+final identifier = 'my client identifier';
+final secret = 'my client secret';
// Calling the top-level `clientCredentialsGrant` function will return a
// [Client] instead.
@@ -181,7 +190,8 @@
// With an authenticated client, you can make requests, and the `Bearer` token
// returned by the server during the client credentials grant will be attached
// to any request you make.
-var response = await client.read("https://example.com/api/some_resource.json");
+var response =
+ await client.read('https://example.com/api/some_resource.json');
// You can save the client's credentials, which consists of an access token, and
// potentially a refresh token and expiry date, to a file. This way, subsequent runs
@@ -191,17 +201,19 @@
```
## Resource Owner Password Grant
-**Resources:** [Method summary][resourceOwnerPasswordGrantMethod], [OAuth documentation][resourceOwnerPasswordGrantDocs]
+
+**Resources:** [Method summary][resourceOwnerPasswordGrantMethod],
+[OAuth documentation][resourceOwnerPasswordGrantDocs]
```dart
// This URL is an endpoint that's provided by the authorization server. It's
// usually included in the server's documentation of its OAuth2 API.
final authorizationEndpoint =
- Uri.parse("http://example.com/oauth2/authorization");
+ Uri.parse('http://example.com/oauth2/authorization');
// The user should supply their own username and password.
-final username = "example user";
-final password = "example password";
+final username = 'example user';
+final password = 'example password';
// The authorization server may issue each client a separate client
// identifier and secret, which allows the server to tell which client
@@ -210,8 +222,8 @@
//
// Some servers don't require the client to authenticate itself, in which case
// these should be omitted.
-final identifier = "my client identifier";
-final secret = "my client secret";
+final identifier = 'my client identifier';
+final secret = 'my client secret';
// Make a request to the authorization endpoint that will produce the fully
// authenticated Client.
@@ -220,13 +232,12 @@
identifier: identifier, secret: secret);
// Once you have the client, you can use it just like any other HTTP client.
-var result = await client.read("http://example.com/protected-resources.txt");
+var result = await client.read('http://example.com/protected-resources.txt');
// Once we're done with the client, save the credentials file. This will allow
// us to re-use the credentials and avoid storing the username and password
// directly.
-new File("~/.myapp/credentials.json")
- .writeAsString(client.credentials.toJson());
+File('~/.myapp/credentials.json').writeAsString(client.credentials.toJson());
```
[authorizationCodeGrantDocs]: https://oauth.net/2/grant-types/authorization-code/
diff --git a/example/main.dart b/example/main.dart
new file mode 100644
index 0000000..1560cdd
--- /dev/null
+++ b/example/main.dart
@@ -0,0 +1,95 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:oauth2/oauth2.dart' as oauth2;
+
+// These URLs are endpoints that are provided by the authorization
+// server. They're usually included in the server's documentation of its
+// OAuth2 API.
+final authorizationEndpoint =
+ Uri.parse('http://example.com/oauth2/authorization');
+final tokenEndpoint = Uri.parse('http://example.com/oauth2/token');
+
+// The authorization server will issue each client a separate client
+// identifier and secret, which allows the server to tell which client
+// is accessing it. Some servers may also have an anonymous
+// identifier/secret pair that any client may use.
+//
+// Note that clients whose source code or binary executable is readily
+// available may not be able to make sure the client secret is kept a
+// secret. This is fine; OAuth2 servers generally won't rely on knowing
+// with certainty that a client is who it claims to be.
+final identifier = 'my client identifier';
+final secret = 'my client secret';
+
+// This is a URL on your application's server. The authorization server
+// will redirect the resource owner here once they've authorized the
+// client. The redirection will include the authorization code in the
+// query parameters.
+final redirectUrl = Uri.parse('http://my-site.com/oauth2-redirect');
+
+/// A file in which the users credentials are stored persistently. If the server
+/// issues a refresh token allowing the client to refresh outdated credentials,
+/// these may be valid indefinitely, meaning the user never has to
+/// re-authenticate.
+final credentialsFile = File('~/.myapp/credentials.json');
+
+/// Either load an OAuth2 client from saved credentials or authenticate a new
+/// one.
+Future<oauth2.Client> createClient() async {
+ var exists = await credentialsFile.exists();
+
+ // If the OAuth2 credentials have already been saved from a previous run, we
+ // just want to reload them.
+ if (exists) {
+ var credentials =
+ oauth2.Credentials.fromJson(await credentialsFile.readAsString());
+ return oauth2.Client(credentials, identifier: identifier, secret: secret);
+ }
+
+ // If we don't have OAuth2 credentials yet, we need to get the resource owner
+ // to authorize us. We're assuming here that we're a command-line application.
+ var grant = oauth2.AuthorizationCodeGrant(
+ identifier, authorizationEndpoint, tokenEndpoint,
+ secret: secret);
+
+ // A URL on the authorization server (authorizationEndpoint with some additional
+ // query parameters). Scopes and state can optionally be passed into this method.
+ var authorizationUrl = grant.getAuthorizationUrl(redirectUrl);
+
+ // Redirect the resource owner to the authorization URL. Once the resource
+ // owner has authorized, they'll be redirected to `redirectUrl` with an
+ // authorization code. The `redirect` should cause the browser to redirect to
+ // another URL which should also have a listener.
+ //
+ // `redirect` and `listen` are not shown implemented here.
+ await redirect(authorizationUrl);
+ var responseUrl = await listen(redirectUrl);
+
+ // Once the user is redirected to `redirectUrl`, pass the query parameters to
+ // the AuthorizationCodeGrant. It will validate them and extract the
+ // authorization code to create a new Client.
+ return await grant.handleAuthorizationResponse(responseUrl.queryParameters);
+}
+
+void main() async {
+ var client = await createClient();
+
+ // Once you have a Client, you can use it just like any other HTTP client.
+ print(await client.read('http://example.com/protected-resources.txt'));
+
+ // Once we're done with the client, save the credentials file. This ensures
+ // that if the credentials were automatically refreshed while using the
+ // client, the new credentials are available for the next run of the
+ // program.
+ await credentialsFile.writeAsString(client.credentials.toJson());
+}
+
+Future<void> redirect(Uri url) async {
+ // Client implementation detail
+}
+
+Future<Uri> listen(Uri url) async {
+ // Client implementation detail
+ return null;
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 4648628..856aac8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,5 @@
name: oauth2
-version: 1.6.1
-author: Dart Team <misc@dartlang.org>
+version: 1.6.2-dev
homepage: https://github.com/dart-lang/oauth2
description: >-
A client library for authenticating with a remote service via OAuth2 on