├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example └── oauth1_example.dart ├── lib ├── oauth1.dart └── src │ ├── authorization.dart │ ├── authorization_header.dart │ ├── authorization_header_builder.dart │ ├── authorization_response.dart │ ├── client.dart │ ├── client_credentials.dart │ ├── credentials.dart │ ├── platform.dart │ └── signature_method.dart ├── pubspec.yaml └── test └── authorization_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Stealed from https://github.com/github/gitignore/blob/master/Dart.gitignore 2 | # Don’t commit the following directories created by pub. 3 | .dart_tool/ 4 | .packages 5 | .pub/ 6 | build/ 7 | packages/ 8 | 9 | 10 | # Or the files created by dart2js. 11 | *.dart.js 12 | *.dart.precompiled.js 13 | *.js_ 14 | *.js.deps 15 | *.js.map 16 | 17 | 18 | # Include when developing application packages. 19 | pubspec.lock 20 | 21 | # Directory created by dartdoc 22 | doc/api/ 23 | 24 | .idea/ 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.0 2 | - Support for newer Dart and `http` versions 3 | 4 | ## 2.0.0 5 | - Migrate to null safety 6 | 7 | ## 1.0.6 8 | - Fixed parameter encoding 9 | 10 | ## 1.0.5 11 | - Allow `oauth_callback_confirmed=1` in addition to `oauth_callback_confirmed=true` 12 | - Add example 13 | 14 | ## 1.0.3 15 | - Confirm compatibility with package http 0.12.0 16 | 17 | ## 1.0.2 18 | - Further cleanup for Dart 2 support 19 | 20 | ## 1.0.1 21 | - Support Dart 2 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, kumar8600 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of kumar8600 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OAuth1 2 | =========== 3 | 4 | [![Build Status](https://travis-ci.org/nbspou/dart-oauth1.svg?branch=fork/nbspou)](https://travis-ci.org/nbspou/dart-oauth1) 5 | 6 | "[RFC 5849: The OAuth 1.0 Protocol][rfc5849]" client implementation for dart 7 | 8 | Usage 9 | ----- 10 | 11 | Add to `pubspec.yaml`: 12 | 13 | ```yaml 14 | dependencies: 15 | oauth1: ^2.1.0 16 | ``` 17 | 18 | Please use like below. 19 | 20 | ```dart 21 | import 'dart:io'; 22 | import 'package:oauth1/oauth1.dart' as oauth1; 23 | 24 | void main() { 25 | // define platform (server) 26 | var platform = new oauth1.Platform( 27 | 'https://api.twitter.com/oauth/request_token', // temporary credentials request 28 | 'https://api.twitter.com/oauth/authorize', // resource owner authorization 29 | 'https://api.twitter.com/oauth/access_token', // token credentials request 30 | oauth1.SignatureMethods.hmacSha1 // signature method 31 | ); 32 | 33 | // define client credentials (consumer keys) 34 | const String apiKey = 'LLDeVY0ySvjoOVmJ2XgBItvTV'; 35 | const String apiSecret = 'JmEpkWXXmY7BYoQor5AyR84BD2BiN47GIBUPXn3bopZqodJ0MV'; 36 | var clientCredentials = new oauth1.ClientCredentials(apiKey, apiSecret); 37 | 38 | // create Authorization object with client credentials and platform definition 39 | var auth = new oauth1.Authorization(clientCredentials, platform); 40 | 41 | // request temporary credentials (request tokens) 42 | auth.requestTemporaryCredentials('oob').then((res) { 43 | // redirect to authorization page 44 | print("Open with your browser: ${auth.getResourceOwnerAuthorizationURI(res.credentials.token)}"); 45 | 46 | // get verifier (PIN) 47 | stdout.write("PIN: "); 48 | String verifier = stdin.readLineSync(); 49 | 50 | // request token credentials (access tokens) 51 | return auth.requestTokenCredentials(res.credentials, verifier); 52 | }).then((res) { 53 | // yeah, you got token credentials 54 | // create Client object 55 | var client = new oauth1.Client(platform.signatureMethod, clientCredentials, res.credentials); 56 | 57 | // now you can access to protected resources via client 58 | client.get('https://api.twitter.com/1.1/statuses/home_timeline.json?count=1').then((res) { 59 | print(res.body); 60 | }); 61 | 62 | // NOTE: you can get optional values from AuthorizationResponse object 63 | print("Your screen name is " + res.optionalParameters['screen_name']); 64 | }); 65 | } 66 | ``` 67 | 68 | In addition, You should save and load the granted token credentials from your drive. Of cource, you don't need to authorize when you did it. 69 | 70 | [rfc5849]: https://tools.ietf.org/html/rfc5849 71 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /example/oauth1_example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:http/src/response.dart'; 4 | import 'package:oauth1/oauth1.dart' as oauth1; 5 | 6 | void main() { 7 | // define platform (server) 8 | final oauth1.Platform platform = oauth1.Platform( 9 | 'https://api.twitter.com/oauth/request_token', // temporary credentials request 10 | 'https://api.twitter.com/oauth/authorize', // resource owner authorization 11 | 'https://api.twitter.com/oauth/access_token', // token credentials request 12 | oauth1.SignatureMethods.hmacSha1 // signature method 13 | ); 14 | 15 | // define client credentials (consumer keys) 16 | const String apiKey = 'LLDeVY0ySvjoOVmJ2XgBItvTV'; 17 | const String apiSecret = 'JmEpkWXXmY7BYoQor5AyR84BD2BiN47GIBUPXn3bopZqodJ0MV'; 18 | final oauth1.ClientCredentials clientCredentials = 19 | oauth1.ClientCredentials(apiKey, apiSecret); 20 | 21 | // create Authorization object with client credentials and platform definition 22 | final oauth1.Authorization auth = 23 | oauth1.Authorization(clientCredentials, platform); 24 | 25 | // request temporary credentials (request tokens) 26 | auth 27 | .requestTemporaryCredentials('oob') 28 | .then((oauth1.AuthorizationResponse res) { 29 | // redirect to authorization page 30 | print('Open with your browser:' 31 | '${auth.getResourceOwnerAuthorizationURI(res.credentials.token)}'); 32 | 33 | // get verifier (PIN) 34 | stdout.write('PIN: '); 35 | final String verifier = stdin.readLineSync() ?? ''; 36 | 37 | // request token credentials (access tokens) 38 | return auth.requestTokenCredentials(res.credentials, verifier); 39 | }).then((oauth1.AuthorizationResponse res) { 40 | // yeah, you got token credentials 41 | // create Client object 42 | final oauth1.Client client = oauth1.Client( 43 | platform.signatureMethod, clientCredentials, res.credentials); 44 | 45 | // now you can access to protected resources via client 46 | client 47 | .get(Uri.parse( 48 | 'https://api.twitter.com/1.1/statuses/home_timeline.json?count=1')) 49 | .then((Response res) { 50 | print(res.body); 51 | }); 52 | 53 | // NOTE: you can get optional values from AuthorizationResponse object 54 | print('Your screen name is ${res.optionalParameters['screen_name']!}'); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /lib/oauth1.dart: -------------------------------------------------------------------------------- 1 | library oauth1; 2 | 3 | export 'src/client_credentials.dart'; 4 | export 'src/credentials.dart'; 5 | export 'src/signature_method.dart'; 6 | export 'src/client.dart'; 7 | export 'src/authorization.dart'; 8 | export 'src/authorization_response.dart'; 9 | export 'src/platform.dart'; 10 | -------------------------------------------------------------------------------- /lib/src/authorization.dart: -------------------------------------------------------------------------------- 1 | library authorization; 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:http/http.dart' as http; 6 | 7 | import 'authorization_header_builder.dart'; 8 | import 'authorization_response.dart'; 9 | import 'client_credentials.dart'; 10 | import 'credentials.dart'; 11 | import 'platform.dart'; 12 | 13 | /// A proxy class describing OAuth 1.0 redirection-based authorization. 14 | /// http://tools.ietf.org/html/rfc5849#section-2 15 | /// 16 | /// Redirection works are responded to client. 17 | /// So you can do PIN-based authorization too if you want. 18 | class Authorization { 19 | final ClientCredentials _clientCredentials; 20 | final Platform _platform; 21 | final http.BaseClient _httpClient; 22 | 23 | /// A constructor of Authorization. 24 | /// 25 | /// If you want to use in web browser, pass http.BrowserClient object for httpClient. 26 | /// https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/http/http-browser_client.BrowserClient 27 | Authorization(this._clientCredentials, this._platform, 28 | [http.BaseClient? httpClient]) 29 | : _httpClient = httpClient ?? http.Client() as http.BaseClient; 30 | 31 | /// Obtain a set of temporary credentials from the server. 32 | /// http://tools.ietf.org/html/rfc5849#section-2.1 33 | /// 34 | /// If not callbackURI passed, authentication becomes PIN-based. 35 | Future requestTemporaryCredentials( 36 | [String? callbackURI]) async { 37 | callbackURI ??= 'oob'; 38 | final Map additionalParams = { 39 | 'oauth_callback': callbackURI 40 | }; 41 | final AuthorizationHeaderBuilder ahb = AuthorizationHeaderBuilder(); 42 | ahb.signatureMethod = _platform.signatureMethod; 43 | ahb.clientCredentials = _clientCredentials; 44 | ahb.method = 'POST'; 45 | ahb.url = _platform.temporaryCredentialsRequestURI; 46 | ahb.additionalParameters = additionalParams; 47 | 48 | final http.Response res = await _httpClient.post( 49 | Uri.parse(_platform.temporaryCredentialsRequestURI), 50 | headers: {'Authorization': ahb.build().toString()}); 51 | 52 | if (res.statusCode != 200) { 53 | throw StateError(res.body); 54 | } 55 | 56 | final Map params = Uri.splitQueryString(res.body); 57 | final String? confirmed = params['oauth_callback_confirmed']; 58 | if (confirmed != 'true' && confirmed != '1') { 59 | throw StateError('oauth_callback_confirmed must be true'); 60 | } 61 | 62 | return AuthorizationResponse.fromMap(params); 63 | } 64 | 65 | /// Get resource owner authorization URI. 66 | /// http://tools.ietf.org/html/rfc5849#section-2.2 67 | String getResourceOwnerAuthorizationURI( 68 | String temporaryCredentialsIdentifier) { 69 | return '${_platform.resourceOwnerAuthorizationURI}' 70 | '?oauth_token=${Uri.encodeComponent(temporaryCredentialsIdentifier)}'; 71 | } 72 | 73 | /// Obtain a set of token credentials from the server. 74 | /// http://tools.ietf.org/html/rfc5849#section-2.3 75 | Future requestTokenCredentials( 76 | Credentials tokenCredentials, String verifier) async { 77 | final Map additionalParams = { 78 | 'oauth_verifier': verifier 79 | }; 80 | final AuthorizationHeaderBuilder ahb = AuthorizationHeaderBuilder(); 81 | ahb.signatureMethod = _platform.signatureMethod; 82 | ahb.clientCredentials = _clientCredentials; 83 | ahb.credentials = tokenCredentials; 84 | ahb.method = 'POST'; 85 | ahb.url = _platform.tokenCredentialsRequestURI; 86 | ahb.additionalParameters = additionalParams; 87 | 88 | final http.Response res = await _httpClient.post( 89 | Uri.parse(_platform.tokenCredentialsRequestURI), 90 | headers: {'Authorization': ahb.build().toString()}); 91 | 92 | if (res.statusCode != 200) { 93 | throw StateError(res.body); 94 | } 95 | final Map params = Uri.splitQueryString(res.body); 96 | return AuthorizationResponse.fromMap(params); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/authorization_header.dart: -------------------------------------------------------------------------------- 1 | library authorization_header; 2 | 3 | import 'client_credentials.dart'; 4 | import 'credentials.dart'; 5 | // import 'package:uuid/uuid.dart'; 6 | 7 | import 'signature_method.dart'; 8 | 9 | /// A class describing Authorization Header. 10 | /// http://tools.ietf.org/html/rfc5849#section-3.5.1 11 | class AuthorizationHeader { 12 | final SignatureMethod _signatureMethod; 13 | final ClientCredentials _clientCredentials; 14 | final Credentials? _credentials; 15 | final String _method; 16 | final String _url; 17 | final Map? _additionalParameters; 18 | 19 | // static final _uuid = new Uuid(); 20 | 21 | AuthorizationHeader(this._signatureMethod, this._clientCredentials, 22 | this._credentials, this._method, this._url, this._additionalParameters); 23 | 24 | /// Set Authorization header to request. 25 | /// 26 | /// Below parameters are provided default values: 27 | /// - oauth_signature_method 28 | /// - oauth_signature 29 | /// - oauth_timestamp 30 | /// - oauth_nonce 31 | /// - oauth_version 32 | /// - oauth_consumer_key 33 | /// - oauth_token 34 | /// - oauth_token_secret 35 | /// 36 | /// You can add parameters by _authorizationHeader. 37 | /// (You can override too but I don't recommend.) 38 | @override 39 | String toString() { 40 | final Map params = {}; 41 | 42 | params['oauth_nonce'] = DateTime.now().millisecondsSinceEpoch.toString(); 43 | params['oauth_signature_method'] = _signatureMethod.name; 44 | params['oauth_timestamp'] = 45 | (DateTime.now().millisecondsSinceEpoch / 1000).floor().toString(); 46 | params['oauth_consumer_key'] = _clientCredentials.token; 47 | params['oauth_version'] = '1.0'; 48 | if (_credentials != null) { 49 | params['oauth_token'] = _credentials!.token; 50 | } 51 | params.addAll(_additionalParameters!); 52 | if (!params.containsKey('oauth_signature')) { 53 | params['oauth_signature'] = _createSignature(_method, _url, params); 54 | } 55 | 56 | final String authHeader = 'OAuth ${params.keys.map((String k) { 57 | return '$k="${Uri.encodeComponent(params[k]!)}"'; 58 | }).join(', ')}'; 59 | return authHeader; 60 | } 61 | 62 | /// Percent-encodes the [param]. 63 | /// 64 | /// All characters except uppercase and lowercase letters, digits and the 65 | /// characters `-_.~` are percent-encoded. 66 | /// 67 | /// See https://oauth.net/core/1.0a/#encoding_parameters. 68 | String _encodeParam(String param) { 69 | return Uri.encodeComponent(param) 70 | .replaceAll('!', '%21') 71 | .replaceAll('*', '%2A') 72 | .replaceAll("'", '%27') 73 | .replaceAll('(', '%28') 74 | .replaceAll(')', '%29'); 75 | } 76 | 77 | /// Create signature in ways referred from 78 | /// https://dev.twitter.com/docs/auth/creating-signature. 79 | String _createSignature( 80 | String method, String url, Map params) { 81 | // Referred from https://dev.twitter.com/docs/auth/creating-signature 82 | if (params.isEmpty) { 83 | throw ArgumentError('params is empty.'); 84 | } 85 | final Uri uri = Uri.parse(url); 86 | 87 | // 88 | // Collecting parameters 89 | // 90 | 91 | // 1. Percent encode every key and value 92 | // that will be signed. 93 | final Map encodedParams = {}; 94 | params.forEach((String k, String v) { 95 | encodedParams[_encodeParam(k)] = _encodeParam(v); 96 | }); 97 | uri.queryParameters.forEach((String k, String v) { 98 | encodedParams[_encodeParam(k)] = _encodeParam(v); 99 | }); 100 | params.remove('realm'); 101 | 102 | // 2. Sort the list of parameters alphabetically[1] 103 | // by encoded key[2]. 104 | final List sortedEncodedKeys = encodedParams.keys.toList()..sort(); 105 | 106 | // 3. For each key/value pair: 107 | // 4. Append the encoded key to the output string. 108 | // 5. Append the '=' character to the output string. 109 | // 6. Append the encoded value to the output string. 110 | // 7. If there are more key/value pairs remaining, 111 | // append a '&' character to the output string. 112 | final String baseParams = sortedEncodedKeys.map((String k) { 113 | return '$k=${encodedParams[k]}'; 114 | }).join('&'); 115 | 116 | // 117 | // Creating the signature base string 118 | // 119 | 120 | final StringBuffer base = StringBuffer(); 121 | // 1. Convert the HTTP Method to uppercase and set the 122 | // output string equal to this value. 123 | base.write(method.toUpperCase()); 124 | 125 | // 2. Append the '&' character to the output string. 126 | base.write('&'); 127 | 128 | // 3. Percent encode the URL origin and path, and append it to the 129 | // output string. 130 | base.write(Uri.encodeComponent(uri.origin + uri.path)); 131 | 132 | // 4. Append the '&' character to the output string. 133 | base.write('&'); 134 | 135 | // 5. Percent encode the parameter string and append it 136 | // to the output string. 137 | base.write(Uri.encodeComponent(baseParams.toString())); 138 | 139 | // 140 | // Getting a signing key 141 | // 142 | 143 | // The signing key is simply the percent encoded consumer 144 | // secret, followed by an ampersand character '&', 145 | // followed by the percent encoded token secret: 146 | final String consumerSecret = 147 | Uri.encodeComponent(_clientCredentials.tokenSecret); 148 | final String tokenSecret = _credentials != null 149 | ? Uri.encodeComponent(_credentials!.tokenSecret) 150 | : ''; 151 | final String signingKey = '$consumerSecret&$tokenSecret'; 152 | 153 | // 154 | // Calculating the signature 155 | // 156 | return _signatureMethod.sign(signingKey, base.toString()); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /lib/src/authorization_header_builder.dart: -------------------------------------------------------------------------------- 1 | library auhthorization_header_builder; 2 | 3 | import 'authorization_header.dart'; 4 | import 'client_credentials.dart'; 5 | import 'credentials.dart'; 6 | import 'signature_method.dart'; 7 | 8 | /// A builder class for AuthorizationHeader 9 | class AuthorizationHeaderBuilder { 10 | SignatureMethod? _signatureMethod; 11 | ClientCredentials? _clientCredentials; 12 | Credentials? _credentials; 13 | String? _method; 14 | String? _url; 15 | Map? _additionalParameters; 16 | 17 | AuthorizationHeaderBuilder(); 18 | AuthorizationHeaderBuilder.from(AuthorizationHeaderBuilder other) 19 | : _signatureMethod = other._signatureMethod, 20 | _clientCredentials = other._clientCredentials, 21 | _credentials = other._credentials, 22 | _method = other._method, 23 | _url = other._url, 24 | _additionalParameters = other._additionalParameters; 25 | 26 | set signatureMethod(SignatureMethod value) => _signatureMethod = value; 27 | set clientCredentials(ClientCredentials value) => _clientCredentials = value; 28 | set credentials(Credentials? value) => _credentials = value; 29 | set method(String value) => _method = value; 30 | set url(String value) => _url = value; 31 | set additionalParameters(Map value) => 32 | _additionalParameters = value; 33 | 34 | AuthorizationHeader build() { 35 | if (_signatureMethod == null) { 36 | throw StateError('signatureMethod is not set'); 37 | } 38 | if (_clientCredentials == null) { 39 | throw StateError('clientCredentials is not set'); 40 | } 41 | if (_method == null) { 42 | throw StateError('method is not set'); 43 | } 44 | if (_url == null) { 45 | throw StateError('url is not set'); 46 | } 47 | return AuthorizationHeader(_signatureMethod!, _clientCredentials!, 48 | _credentials, _method!, _url!, _additionalParameters); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/authorization_response.dart: -------------------------------------------------------------------------------- 1 | library authorization_response; 2 | 3 | import 'credentials.dart'; 4 | 5 | /// A class describing Response of Authorization request. 6 | class AuthorizationResponse { 7 | final Credentials _credentials; 8 | final Map _optionalParameters; 9 | 10 | AuthorizationResponse(this._credentials, this._optionalParameters); 11 | factory AuthorizationResponse.fromMap(Map parameters) { 12 | final Map paramsCopy = Map.from(parameters); 13 | final Credentials cred = Credentials.fromMap(paramsCopy); 14 | paramsCopy.remove('oauth_token'); 15 | paramsCopy.remove('oauth_token_secret'); 16 | return AuthorizationResponse(cred, paramsCopy); 17 | } 18 | 19 | Credentials get credentials => _credentials; 20 | Map get optionalParameters => _optionalParameters; 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/client.dart: -------------------------------------------------------------------------------- 1 | library oauth1_client; 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:http/http.dart' as http; 6 | 7 | import 'authorization_header_builder.dart'; 8 | import 'client_credentials.dart'; 9 | import 'credentials.dart'; 10 | import 'signature_method.dart'; 11 | 12 | /// A proxy class describing OAuth 1.0 Authenticated Request 13 | /// http://tools.ietf.org/html/rfc5849#section-3 14 | /// 15 | /// If _credentials is null, this is usable for authorization requests too. 16 | class Client extends http.BaseClient { 17 | final SignatureMethod _signatureMethod; 18 | final ClientCredentials _clientCredentials; 19 | final Credentials? _credentials; 20 | final http.BaseClient _httpClient; 21 | 22 | /// A constructor of Client. 23 | /// 24 | /// If you want to use in web browser, pass http.BrowserClient object for httpClient. 25 | /// https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/http/http-browser_client.BrowserClient 26 | Client(this._signatureMethod, this._clientCredentials, this._credentials, 27 | [http.BaseClient? httpClient]) 28 | : _httpClient = httpClient ?? http.Client() as http.BaseClient; 29 | 30 | @override 31 | Future send(http.BaseRequest request) { 32 | final AuthorizationHeaderBuilder ahb = AuthorizationHeaderBuilder(); 33 | ahb.signatureMethod = _signatureMethod; 34 | ahb.clientCredentials = _clientCredentials; 35 | ahb.credentials = _credentials; 36 | ahb.method = request.method; 37 | ahb.url = request.url.toString(); 38 | final Map headers = request.headers; 39 | Map additionalParameters = {}; 40 | if (headers.containsKey('Authorization')) { 41 | additionalParameters = Uri.splitQueryString(headers['Authorization']!); 42 | } 43 | if (headers.containsKey('content-type') && 44 | headers['content-type']! 45 | .contains('application/x-www-form-urlencoded')) { 46 | additionalParameters 47 | .addAll(Uri.splitQueryString((request as http.Request).body)); 48 | } 49 | ahb.additionalParameters = additionalParameters; 50 | 51 | request.headers['Authorization'] = ahb.build().toString(); 52 | return _httpClient.send(request); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/client_credentials.dart: -------------------------------------------------------------------------------- 1 | library client_credentials; 2 | 3 | /// A class describing OAuth client credentials. 4 | class ClientCredentials { 5 | final String _token; 6 | final String _tokenSecret; 7 | 8 | ClientCredentials(this._token, this._tokenSecret); 9 | 10 | String get token => _token; 11 | String get tokenSecret => _tokenSecret; 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/credentials.dart: -------------------------------------------------------------------------------- 1 | library credentials; 2 | 3 | import 'dart:convert'; 4 | 5 | /// A class describing OAuth credentials except for client credential 6 | class Credentials { 7 | final String _token; 8 | final String _tokenSecret; 9 | 10 | Credentials(this._token, this._tokenSecret); 11 | factory Credentials.fromMap(Map parameters) { 12 | if (!parameters.containsKey('oauth_token')) { 13 | throw ArgumentError("params doesn't have a key 'oauth_token'"); 14 | } 15 | if (!parameters.containsKey('oauth_token_secret')) { 16 | throw ArgumentError("params doesn't have a key 'oauth_token_secret'"); 17 | } 18 | return Credentials( 19 | parameters['oauth_token']!, parameters['oauth_token_secret']!); 20 | } 21 | factory Credentials.fromJSON(String jstr) { 22 | return Credentials.fromMap(json.decode(jstr)); 23 | } 24 | 25 | String get token => _token; 26 | String get tokenSecret => _tokenSecret; 27 | 28 | @override 29 | String toString() { 30 | return 'oauth_token=$token&oauth_token_secret=$tokenSecret'; 31 | } 32 | 33 | Map toJSON() { 34 | return { 35 | 'oauth_token': token, 36 | 'oauth_token_secret': tokenSecret 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/platform.dart: -------------------------------------------------------------------------------- 1 | library platform; 2 | 3 | import 'signature_method.dart'; 4 | 5 | /// Configuration of OAuth1Authorization. 6 | /// http://tools.ietf.org/html/rfc5849 7 | class Platform { 8 | final String _temporaryCredentialsRequestURI; 9 | final String _resourceOwnerAuthorizationURI; 10 | final String _tokenCredentialsRequestURI; 11 | final SignatureMethod _signatureMethod; 12 | 13 | Platform( 14 | this._temporaryCredentialsRequestURI, 15 | this._resourceOwnerAuthorizationURI, 16 | this._tokenCredentialsRequestURI, 17 | this._signatureMethod); 18 | 19 | /// Temporary Credentials Request URI 20 | /// http://tools.ietf.org/html/rfc5849#section-2.1 21 | String get temporaryCredentialsRequestURI => _temporaryCredentialsRequestURI; 22 | 23 | /// Resource Owner Authorization URI 24 | /// http://tools.ietf.org/html/rfc5849#section-2.2 25 | String get resourceOwnerAuthorizationURI => _resourceOwnerAuthorizationURI; 26 | 27 | /// Token Credentials Request URI 28 | /// http://tools.ietf.org/html/rfc5849#section-2.3 29 | String get tokenCredentialsRequestURI => _tokenCredentialsRequestURI; 30 | 31 | /// Signature Method 32 | /// http://tools.ietf.org/html/rfc5849#section-3.4 33 | SignatureMethod get signatureMethod => _signatureMethod; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/signature_method.dart: -------------------------------------------------------------------------------- 1 | library signature_method; 2 | 3 | import 'dart:convert'; 4 | import 'package:crypto/crypto.dart'; 5 | 6 | /// A class abstracting Signature Method. 7 | /// http://tools.ietf.org/html/rfc5849#section-3.4 8 | class SignatureMethod { 9 | final String _name; 10 | final String Function(String key, String text) _sign; 11 | 12 | /// A constructor of SignatureMethod. 13 | SignatureMethod(this._name, this._sign); 14 | 15 | /// Signature Method Name 16 | String get name => _name; 17 | 18 | /// Sign data by key. 19 | String sign(String key, String text) => _sign(key, text); 20 | } 21 | 22 | /// A abstract class contains Signature Methods. 23 | abstract class SignatureMethods { 24 | /// http://tools.ietf.org/html/rfc5849#section-3.4.2 25 | static final SignatureMethod hmacSha1 = 26 | SignatureMethod('HMAC-SHA1', (String key, String text) { 27 | final Hmac hmac = Hmac(sha1, key.codeUnits); 28 | final List bytes = hmac.convert(text.codeUnits).bytes; 29 | 30 | // The output of the HMAC signing function is a binary 31 | // string. This needs to be base64 encoded to produce 32 | // the signature string. 33 | return base64.encode(bytes); 34 | }); 35 | 36 | /// http://tools.ietf.org/html/rfc5849#section-3.4.3 37 | /// TODO: Implement RSA-SHA1 38 | 39 | /// http://tools.ietf.org/html/rfc5849#section-3.4.4 40 | static final SignatureMethod plaintext = 41 | SignatureMethod('PLAINTEXT', (String key, String text) { 42 | return key; 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: oauth1 2 | version: 2.1.0 3 | description: "\"RFC 5849: The OAuth 1.0 Protocol\" client implementation for Dart." 4 | homepage: https://github.com/nbspou/dart-oauth1 5 | repository: https://github.com/nbspou/dart-oauth1 6 | issue_tracker: https://github.com/nbspou/dart-oauth1/issues 7 | topics: 8 | - authentication 9 | - network 10 | - oauth 11 | - web 12 | 13 | environment: 14 | sdk: '>=2.12.0 <4.0.0' 15 | 16 | dependencies: 17 | crypto: ^3.0.0 18 | http: '>=0.13.0 <2.0.0' 19 | 20 | dev_dependencies: 21 | lints: ^5.1.0 22 | test: ^1.25.8 23 | -------------------------------------------------------------------------------- /test/authorization_test.dart: -------------------------------------------------------------------------------- 1 | library authorization_test; 2 | 3 | import 'package:http/http.dart' as http; 4 | import 'package:oauth1/oauth1.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | void main() { 8 | final Platform twitter1_1 = Platform( 9 | 'https://api.twitter.com/oauth/request_token', 10 | 'https://api.twitter.com/oauth/authorize', 11 | 'https://api.twitter.com/oauth/access_token', 12 | SignatureMethods.hmacSha1); 13 | const String apiKey = 'LLDeVY0ySvjoOVmJ2XgBItvTV'; 14 | const String apiSecret = 'JmEpkWXXmY7BYoQor5AyR84BD2BiN47GIBUPXn3bopZqodJ0MV'; 15 | final ClientCredentials clientCredentials = 16 | ClientCredentials(apiKey, apiSecret); 17 | 18 | test('request temporary credentials', () { 19 | final Authorization auth = Authorization( 20 | clientCredentials, twitter1_1, http.Client() as http.BaseClient?); 21 | return auth.requestTemporaryCredentials(); 22 | }); 23 | } 24 | --------------------------------------------------------------------------------