Skip to content
This repository has been archived by the owner on Aug 26, 2024. It is now read-only.

Commit

Permalink
use package:lints; rev pubspec version (#134)
Browse files Browse the repository at this point in the history
* use package:lints; rev pubspec version

* use package:lints 2.0.0

* rev CI to 2.17

* use super initializers
  • Loading branch information
devoncarew authored Oct 25, 2022
1 parent 199ebf1 commit 4172ae5
Show file tree
Hide file tree
Showing 23 changed files with 208 additions and 133 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Dependabot configuration file.
version: 2

updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
11 changes: 0 additions & 11 deletions .github/dependabot.yml

This file was deleted.

12 changes: 6 additions & 6 deletions .github/workflows/test-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
strategy:
fail-fast: false
matrix:
sdk: [2.12.0, dev]
sdk: [2.17.0, dev]
steps:
- uses: actions/checkout@v3
- uses: dart-lang/setup-dart@v1
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d
with:
sdk: ${{ matrix.sdk }}
- id: install
Expand All @@ -50,10 +50,10 @@ jobs:
matrix:
# Add macos-latest and/or windows-latest if relevant for this package.
os: [ubuntu-latest]
sdk: [2.12.0, dev]
sdk: [2.17.0, dev]
steps:
- uses: actions/checkout@v3
- uses: dart-lang/setup-dart@v1
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d
with:
sdk: ${{ matrix.sdk }}
- id: install
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# 2.0.1-dev
# 2.0.1

* Handle `expires_in` when encoded as string.
* Populate the pubspec `repository` field.
* Increase the minimum Dart SDK to `2.17.0`.

# 2.0.0

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2014, the Dart project authors.
Copyright 2014, the Dart project authors.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
[![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)
[![pub package](https://img.shields.io/pub/v/oauth2.svg)](https://pub.dev/packages/oauth2)
[![package publisher](https://img.shields.io/pub/publisher/oauth2.svg)](https://pub.dev/packages/oauth2/publisher)

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.

## About OAuth2

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
Expand Down
41 changes: 40 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,40 @@
include: package:pedantic/analysis_options.yaml
include: package:lints/recommended.yaml

analyzer:
language:
strict-casts: true
strict-raw-types: true

linter:
rules:
- always_declare_return_types
- avoid_catching_errors
- avoid_dynamic_calls
- avoid_private_typedef_functions
- avoid_unused_constructor_parameters
- avoid_void_async
- cancel_subscriptions
- directives_ordering
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_runtimeType_toString
- omit_local_variable_types
- only_throw_errors
- package_api_docs
- prefer_asserts_in_initializer_lists
- prefer_const_constructors
- prefer_const_declarations
- prefer_relative_imports
- prefer_single_quotes
- sort_pub_dependencies
- test_types_in_equals
- throw_in_finally
- type_annotate_public_apis
- unawaited_futures
- unnecessary_await_in_return
- unnecessary_lambdas
- unnecessary_parenthesis
- unnecessary_statements
- use_is_even_rather_than_modulo
- use_string_buffers
- use_super_parameters
11 changes: 6 additions & 5 deletions example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ final tokenEndpoint = Uri.parse('http://example.com/oauth2/token');
// 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';
const identifier = 'my client identifier';
const 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
Expand Down Expand Up @@ -53,8 +53,9 @@ Future<oauth2.Client> createClient() async {
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.
// 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
Expand All @@ -69,7 +70,7 @@ Future<oauth2.Client> createClient() async {
// 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);
return grant.handleAuthorizationResponse(responseUrl.queryParameters);
}

void main() async {
Expand Down
6 changes: 3 additions & 3 deletions lib/oauth2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.

export 'src/authorization_code_grant.dart';
export 'src/client_credentials_grant.dart';
export 'src/resource_owner_password_grant.dart';
export 'src/authorization_exception.dart';
export 'src/client.dart';
export 'src/client_credentials_grant.dart';
export 'src/credentials.dart';
export 'src/authorization_exception.dart';
export 'src/expiration_exception.dart';
export 'src/resource_owner_password_grant.dart';
33 changes: 18 additions & 15 deletions lib/src/authorization_code_grant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';

import 'client.dart';
import 'authorization_exception.dart';
import 'client.dart';
import 'credentials.dart';
import 'handle_access_token_response.dart';
import 'parameters.dart';
Expand Down Expand Up @@ -107,7 +107,8 @@ class AuthorizationCodeGrant {
static const String _charset =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';

/// The PKCE code verifier. Will be generated if one is not provided in the constructor.
/// The PKCE code verifier. Will be generated if one is not provided in the
/// constructor.
final String _codeVerifier;

/// Creates a new grant.
Expand All @@ -126,8 +127,8 @@ class AuthorizationCodeGrant {
/// [onCredentialsRefreshed] will be called by the constructed [Client]
/// whenever the credentials are refreshed.
///
/// [codeVerifier] String to be used as PKCE code verifier. If none is provided a
/// random codeVerifier will be generated.
/// [codeVerifier] String to be used as PKCE code verifier. If none is
/// provided a random codeVerifier will be generated.
/// The codeVerifier must meet requirements specified in [RFC 7636].
///
/// [RFC 7636]: https://tools.ietf.org/html/rfc7636#section-4.1
Expand Down Expand Up @@ -221,10 +222,11 @@ class AuthorizationCodeGrant {
/// [getAuthorizationUrl] is called, or to call it after
/// [handleAuthorizationCode] is called.
///
/// Throws [FormatException] if [parameters] is invalid according to the OAuth2
/// spec or if the authorization server otherwise provides invalid responses.
/// If `state` was passed to [getAuthorizationUrl], this will throw a
/// [FormatException] if the `state` parameter doesn't match the original value.
/// Throws [FormatException] if [parameters] is invalid according to the
/// OAuth2 spec or if the authorization server otherwise provides invalid
/// responses. If `state` was passed to [getAuthorizationUrl], this will throw
/// a [FormatException] if the `state` parameter doesn't match the original
/// value.
///
/// Throws [AuthorizationException] if the authorization fails.
Future<Client> handleAuthorizationResponse(
Expand Down Expand Up @@ -259,7 +261,7 @@ class AuthorizationCodeGrant {
'"code".');
}

return await _handleAuthorizationCode(parameters['code']);
return _handleAuthorizationCode(parameters['code']);
}

/// Processes an authorization code directly.
Expand All @@ -285,7 +287,7 @@ class AuthorizationCodeGrant {
}
_state = _State.finished;

return await _handleAuthorizationCode(authorizationCode);
return _handleAuthorizationCode(authorizationCode);
}

/// This works just like [handleAuthorizationCode], except it doesn't validate
Expand Down Expand Up @@ -326,11 +328,12 @@ class AuthorizationCodeGrant {
onCredentialsRefreshed: _onCredentialsRefreshed);
}

/// Randomly generate a 128 character string to be used as the PKCE code verifier
static String _createCodeVerifier() {
return List.generate(
128, (i) => _charset[Random.secure().nextInt(_charset.length)]).join();
}
// Randomly generate a 128 character string to be used as the PKCE code
// verifier.
static String _createCodeVerifier() => List.generate(
128,
(i) => _charset[Random.secure().nextInt(_charset.length)],
).join();

/// Closes the grant and frees its resources.
///
Expand Down
35 changes: 22 additions & 13 deletions lib/src/credentials.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,25 +128,28 @@ class Credentials {
///
/// Throws a [FormatException] if the JSON is incorrectly formatted.
factory Credentials.fromJson(String json) {
void validate(condition, message) {
void validate(bool condition, message) {
if (condition) return;
throw FormatException('Failed to load credentials: $message.\n\n$json');
}

var parsed;
dynamic parsed;
try {
parsed = jsonDecode(json);
} on FormatException {
validate(false, 'invalid JSON');
}

validate(parsed is Map, 'was not a JSON map');

parsed = parsed as Map;
validate(parsed.containsKey('accessToken'),
'did not contain required field "accessToken"');
validate(
parsed['accessToken'] is String,
'required field "accessToken" was not a string, was '
'${parsed["accessToken"]}');
parsed['accessToken'] is String,
'required field "accessToken" was not a string, was '
'${parsed["accessToken"]}',
);

for (var stringField in ['refreshToken', 'idToken', 'tokenEndpoint']) {
var value = parsed[stringField];
Expand All @@ -159,22 +162,28 @@ class Credentials {
'field "scopes" was not a list, was "$scopes"');

var tokenEndpoint = parsed['tokenEndpoint'];
Uri? tokenEndpointUri;
if (tokenEndpoint != null) {
tokenEndpoint = Uri.parse(tokenEndpoint);
tokenEndpointUri = Uri.parse(tokenEndpoint as String);
}

var expiration = parsed['expiration'];
DateTime? expirationDateTime;
if (expiration != null) {
validate(expiration is int,
'field "expiration" was not an int, was "$expiration"');
expiration = DateTime.fromMillisecondsSinceEpoch(expiration);
expiration = expiration as int;
expirationDateTime = DateTime.fromMillisecondsSinceEpoch(expiration);
}

return Credentials(parsed['accessToken'],
refreshToken: parsed['refreshToken'],
idToken: parsed['idToken'],
tokenEndpoint: tokenEndpoint,
scopes: (scopes as List).map((scope) => scope as String),
expiration: expiration);
return Credentials(
parsed['accessToken'] as String,
refreshToken: parsed['refreshToken'] as String?,
idToken: parsed['idToken'] as String?,
tokenEndpoint: tokenEndpointUri,
scopes: (scopes as List).map((scope) => scope as String),
expiration: expirationDateTime,
);
}

/// Serializes a set of credentials to JSON.
Expand Down
32 changes: 17 additions & 15 deletions lib/src/handle_access_token_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';

import 'credentials.dart';
import 'authorization_exception.dart';
import 'credentials.dart';
import 'parameters.dart';

/// The amount of time to add as a "grace period" for credential expiration.
Expand Down Expand Up @@ -43,7 +43,7 @@ Credentials handleAccessTokenResponse(http.Response response, Uri tokenEndpoint,

var contentTypeString = response.headers['content-type'];
if (contentTypeString == null) {
throw FormatException('Missing Content-Type string.');
throw const FormatException('Missing Content-Type string.');
}

var parameters =
Expand All @@ -62,7 +62,7 @@ Credentials handleAccessTokenResponse(http.Response response, Uri tokenEndpoint,

// TODO(nweiz): support the "mac" token type
// (http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01)
if (parameters['token_type'].toLowerCase() != 'bearer') {
if ((parameters['token_type'] as String).toLowerCase() != 'bearer') {
throw FormatException(
'"$tokenEndpoint": unknown token type "${parameters['token_type']}"');
}
Expand Down Expand Up @@ -95,14 +95,16 @@ Credentials handleAccessTokenResponse(http.Response response, Uri tokenEndpoint,

var expiration = expiresIn == null
? null
: startTime.add(Duration(seconds: expiresIn) - _expirationGrace);

return Credentials(parameters['access_token'],
refreshToken: parameters['refresh_token'],
idToken: parameters['id_token'],
tokenEndpoint: tokenEndpoint,
scopes: scopes,
expiration: expiration);
: startTime.add(Duration(seconds: expiresIn as int) - _expirationGrace);

return Credentials(
parameters['access_token'] as String,
refreshToken: parameters['refresh_token'] as String?,
idToken: parameters['id_token'] as String?,
tokenEndpoint: tokenEndpoint,
scopes: scopes,
expiration: expiration,
);
} on FormatException catch (e) {
throw FormatException('Invalid OAuth response for "$tokenEndpoint": '
'${e.message}.\n\n${response.body}');
Expand Down Expand Up @@ -133,7 +135,7 @@ void _handleErrorResponse(
var parameters = getParameters(contentType, response.body);

if (!parameters.containsKey('error')) {
throw FormatException('did not contain required parameter "error"');
throw const FormatException('did not contain required parameter "error"');
} else if (parameters['error'] is! String) {
throw FormatException('required parameter "error" was not a string, was '
'"${parameters["error"]}"');
Expand All @@ -147,8 +149,8 @@ void _handleErrorResponse(
}
}

var description = parameters['error_description'];
var uriString = parameters['error_uri'];
var uriString = parameters['error_uri'] as String?;
var uri = uriString == null ? null : Uri.parse(uriString);
throw AuthorizationException(parameters['error'], description, uri);
var description = parameters['error_description'] as String?;
throw AuthorizationException(parameters['error'] as String, description, uri);
}
Loading

0 comments on commit 4172ae5

Please sign in to comment.