Pinterest Oauth2 Access Token Issue - flutter

I have a Flutter app that I'm trying to integrate with Pinterest, and I'm a little stuck.
I have code to request an access token and, while the code does get an access token, that token does not appear to be useful. Any API that I call with that token results in a 308, and if I go to the Pinterest developer site and debug the token, then it looks like this:
So, it's like the token has no scopes and was not issued for an actual application, which is very weird. The code I have looks like this:
Future<String> _login() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
accessToken = null;
if (accessToken == null) {
// accessToken = prefs.get(ACCESS_TOKEN_KEY);
//If we don't have an existing access token, get a new one.
if (accessToken == null) {
final appId = "myappid";
final secret =
"mysecret";
final url = Uri.https('api.pinterest.com', 'oauth', {
'response_type': 'code',
'client_id': appId,
'redirect_uri': "pdk<myappid>://",
'state': 'someBogusStuff',
'scope': 'read_public,write_public',
});
final result = await FlutterWebAuth.authenticate(
url: url.toString(), callbackUrlScheme: 'pdk<myappid>');
print(result);
final tokenEndpoint = Uri.https('api.pinterest.com', 'v1/oauth/token');
// Use the code to get an access token
final response = await http.post(tokenEndpoint, body: {
'client_id': appId,
'client_secret': secret,
'grant_type': 'authorization_code',
'code': Uri
.parse(result)
.queryParameters['code'],
});
if (response.statusCode != 200) {
return response.body;
}
var decodedResponse = jsonDecode(response.body);
print(decodedResponse);
accessToken = decodedResponse['access_token'];
//Save the access token
prefs.setString(ACCESS_TOKEN_KEY, accessToken);
}
}
return getMe(accessToken);
}
Future<String> getMe(String token) async {
final url =
Uri.https('api.pinterest.com', 'v1/me', {'access_token': token});
Completer<String> completer = Completer();
String result;
http.get(url, headers: {'User-Agent': 'PDK 1.0'}).then((response) {
print(response.statusCode);
result = response.body;
}).whenComplete(() => completer.complete(result));
return completer.future;
}
When I print out the result of the call to /oauth/token it looks like I got back a good token:
{access_token: AvtF3MxUy4gbujGGhN_KcYFExQVAFfmOZGmxYN5GkhE-iKDH6QpYADAAAzbHRpc4dD1gvFwAAAAA, token_type: bearer, scope: [read_write_all, read_public, write_public, read_private, write_private]}
But it doesn't work. What am I doing wrong here?

Related

I create flutter with api call but the data not showing at fresh install

So I create an app with rest API, but the data not showing on a fresh install
This is for gettoken and save to shared prefs
getInit() async {
String myUrl = "$serverUrl/get-token";
http.Response response = await http.post(Uri.parse(myUrl),
body: {'secret': 'code'});
debugPrint(response.statusCode.toString());
debugPrint(response.body);
var data = json.decode(response.body)["data"];
_save(data["access_token"]);
// return data;
}
//SAVE TOKEN
_save(String token) async {
final prefs = await SharedPreferences.getInstance();
const key = 'token';
final value = token;
prefs.setString(key, value);
debugPrint("new token save " + value);
}
This for getlist item, need bearer access token from shared prefs
getRecList() async {
final prefs = await SharedPreferences.getInstance();
const key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/home";
http.Response response = await http.get(Uri.parse(myUrl), headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $value'
});
debugPrint(response.body);
if (response.statusCode == 200) {
List data = jsonDecode(response.body)['data'];
List<ModelKost> modelkost =
data.map((item) => ModelKost.fromJson(item)).toList();
return modelkost;
} else {
return <ModelKost>[];
}
}
So every time I fresh install, home page does not show any data because getRecList item is forbidden access...
The log says token success, but getRecList fails because not get access token, it only happens on fresh install if I refresh/hot reload the list showing normally ...
so I guess the function getRecList wrong here, but I have no idea to fix it ...
i think the problem is you are not waiting for token value. use await when geting value from shared preferences
So I create an app with rest API, but the data not showing on a fresh install
getRecList() async {
final prefs = await SharedPreferences.getInstance();
const key = 'token';
final value =await prefs.get(key) ?? 0; //use await here
String myUrl = "$serverUrl/home";
http.Response response = await http.get(Uri.parse(myUrl), headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $value'
});
debugPrint(response.body);
if (response.statusCode == 200) {
List data = jsonDecode(response.body)['data'];
List<ModelKost> modelkost =
data.map((item) => ModelKost.fromJson(item)).toList();
return modelkost;
} else {
return <ModelKost>[];
}
}

Unable to get user profile data from API

I an trying to get the user data from the API and display in the UI using flutter http package. After login in, the API returned 200 OK response. When I tried navigating back to the user profile page to get the logged in user from the API i got a 401 unauthorized access response from the API even after putting the authorization bearer token (gotten from the the login) in the http header it keeps returning 401 response. After the error continued I added a refresh token to the api but the get user keep throwing 401 response.
The Api login service
static Future loginUser(User user) async {
// Try low level Http_Client
HttpClient httpClient = HttpClient();
HttpClientRequest request = await httpClient.postUrl(
Uri.parse('$_baseUrl/login'),
);
request.headers.set('Content-Type', 'application/json');
request.add(
utf8.encode(
jsonEncode({
"userName": user.userName,
"userEmail": user.userEmail,
"password": user.password,
}),
),
);
HttpClientResponse response = await request.close();
String message = await response.transform(utf8.decoder).join();
final jsonResponse = jsonDecode(message);
httpClient.close();
if (response.statusCode == 200 || response.statusCode == 201) {
print('Response: $jsonResponse');
final _token = jsonResponse['Token'];
final _expiration = jsonResponse['Expires'];
await AppStorage.writeKey('token', _token);
print('Token Expiration: $_expiration');
return User.fromJson(jsonDecode(jsonResponse));
} else if (response.statusCode == 401 || response.statusCode == 403) {
print('Error Msg: $jsonResponse');
_refreshToken();
} else {
print("Failed Response : $jsonResponse");
return false;
}
}
The api to get user
static Future getUser() async {
String? token = await AppStorage.getKey('token');
HttpClient httpClient = HttpClient();
HttpClientRequest request = await httpClient.putUrl(
Uri.parse('$_baseUrl/profile'),
);
request.headers.set('Authentication', 'Bearer $token');
request.headers.set('Content-Type', 'application/json');
var response = await request.close();
var message = await response.transform(utf8.decoder).join();
if (message.isEmpty) {
print('Message Is Empty');
return;
} else {
var jsonResponse = json.decode(message);
print('Message: $message');
httpClient.close();
if (response.statusCode == 200 || response.statusCode == 201) {
print('Response: $jsonResponse');
return User.fromJson(jsonResponse);
} else if (response.statusCode == 401 || response.statusCode == 403) {
// If response returns 401 refresh token
print('Error Msg: $jsonResponse');
_refreshToken();
} else {
print("Failed Response : $jsonResponse");
}
}
}
The refresh token api service
static Future<dynamic> _refreshToken() async {
print('Refreshing Token ...');
String? token = await AppStorage.getKey('token');
HttpClient httpClient = HttpClient();
HttpClientRequest request = await httpClient.postUrl(
Uri.parse('$_baseUrl/refresh'),
);
request.headers.set('Content-Type', 'application/json');
request.headers.set('Authentication', 'Bearer $token');
var response = await request.close();
var message = await response.transform(utf8.decoder).join();
var jsonResponse = jsonDecode(message);
httpClient.close();
if (response.statusCode == 200 || response.statusCode == 201) {
print('Response: $jsonResponse');
// Get new token from the response
final _newToken = jsonResponse['Token'];
final _expiration = jsonResponse['Expires'];
// Store the new token
await AppStorage.writeKey('token', _newToken);
print('Token Expiration: $_expiration');
// Retry Get user token
getUser();
} else if (response.statusCode == 401 || response.statusCode == 403) {
// If response returns 401 log the user out
print('Error Msg: $jsonResponse');
_logUserOut();
} else {
print("Failed Response : $jsonResponse");
}
}

Retrieving the Authorization Code from Fitbit API with Flutter

I am trying to build an app with flutter that uses Fitbit API, I tried different packages to do Web Authentication like Fitbitter that uses flutter-web-auth for authentication. Also tried web-view Widget.
in case of Fitbitter :
the issue is when I logged in the fitbit account and get the response that content authorization code https://example.com/callback?code=<authorization_code>#_=_0.
authorize method in FitbitConnector class doesn't redirect me back to the app with authorization code instead stays in the chrome custom tab.
authorize method
static Future<String?> authorize(
{BuildContext? context,
String? clientID,
String? clientSecret,
required String redirectUri,
required String callbackUrlScheme}) async {
// Instantiate Dio and its Response
Dio dio = Dio();
Response response;
String? userID;
// Generate the fitbit url
final fitbitAuthorizeFormUrl = FitbitAuthAPIURL.authorizeForm(
userID: userID, redirectUri: redirectUri, clientID: clientID);
// Perform authentication
try {
final result = await FlutterWebAuth.authenticate(
url: fitbitAuthorizeFormUrl.url!,
callbackUrlScheme: callbackUrlScheme);
//Get the auth code
final code = Uri.parse(result).queryParameters['code'];
// Generate the fitbit url
final fitbitAuthorizeUrl = FitbitAuthAPIURL.authorize(
userID: userID,
redirectUri: redirectUri,
code: code,
clientID: clientID,
clientSecret: clientSecret);
response = await dio.post(
fitbitAuthorizeUrl.url!,
data: fitbitAuthorizeUrl.data,
options: Options(
contentType: Headers.formUrlEncodedContentType,
headers: {
'Authorization': fitbitAuthorizeUrl.authorizationHeader,
},
),
);
// Debugging
final logger = Logger();
logger.i('$response');
// Save authorization tokens
final accessToken = response.data['access_token'] as String;
final refreshToken = response.data['refresh_token'] as String;
userID = response.data['user_id'] as String?;
GetIt.instance<SharedPreferences>()
.setString('fitbitAccessToken', accessToken);
GetIt.instance<SharedPreferences>()
.setString('fitbitRefreshToken', refreshToken);
} catch (e) {
print(e);
} // catch
return userID;
}
Do you know a way to do web authentication and get redirected to the app with user Token and ID?

Flutter: What is the Correct approach to get value from Future?

I have a function that which returns user token, and saves it to shared preference, if token is present it saves it to SP. Another method awaits for the token and if token is present authenticates user
Here is my code
login(username, password) async {
final String url = "http://10.0.2.2:8080/api/auth/signin"; // iOS
final http.Response response = await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'username': username,
'password': password,
}),
);
LoginModel userSave = loginModelFromJson(response.body);
print(response.body);
final SharedPreferences prefs = await SharedPreferences.getInstance();
bool result = await prefs.setString('user', jsonEncode(userSave));
print(result);
}
This piece of code works as expected, but I'm having issue getting the token value from Future.
Case Scenario: User enters username and password and authenticates with server. Incase account exists a token is generated from server and sent to app and stored in shared prefs. token if available is picked from shared prefs and used to login to app, but the check of the token is done before it is generated and saved
Future<LoginModel> getUserInfo() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
Map<String, dynamic> userMap;
final String userStr = prefs.getString('user');
if (userStr != null) {
userMap = jsonDecode(userStr) as Map<String, dynamic>;
}
if (userMap != null) {
final LoginModel user = LoginModel.fromJson(userMap);
print(user);
return user;
}
return null;
}
the token gets called way before it is saved throwing an error of null
RoundedButton(
text: "LOGIN",
press: () async {
if (_formKey.currentState.validate()) {
progressDialog.show();
await login(
username,
password,
);
SharedPreferences prefs =
await SharedPreferences.getInstance();
String token = prefs.getString("accessToken");
getUserInfo();
// ignore: null_aware_in_condition
if (token == null) {
progressDialog.hide();
showAlertsDialog(context);
// ignore: null_aware_in_condition
} else {
progressDialog.hide();
showAlertzDialog(context);
}
}
},
),
I believe there is very small logical mistake, but unable to find it myself.
I believe you cannot directly call jsonEncode with a custom object like the way you do it in jsonEncode(userSave). Do print(jsonEncode(userSave)); to see if the value is properly being converted into a string.

How to get the token from firebase_auth

I'd like to get the auth token from firebase (email and password auth) to authenticate in my firebase cloud function. It seems like the functions getIdToken() and getToken() are both not working for firebase_auth package.
is there an other function or is there even a better idea to make sure only authenticated users can trigger the cloud functions?
var token = await FirebaseAuth.instance.currentUser.getIdToken();
var response = await httpClient.get(url,headers: {'Authorization':"Bearer $token"});
I agree with #Doug on this one - callable wraps this for you and will be easier -, but my use case required me to make HTTPS calls (onRequest in Functions). Also, I think you're just in the correct path - but you're possibly not checking it in your Cloud Functions.
In your app, you'll call:
_httpsCall() async {
// Fetch the currentUser, and then get its id token
final user = await FirebaseAuth.instance.currentUser();
final idToken = await user.getIdToken();
final token = idToken.token;
// Create authorization header
final header = { "authorization": 'Bearer $token' };
get("http://YOUR_PROJECT_BASE_URL/httpsFunction", headers: header)
.then((response) {
final status = response.statusCode;
print('STATUS CODE: $status');
})
.catchError((e) {
print(e);
});
}
In your function, you'll check for the token:
export const httpsFunction = functions.https.onRequest((request, response) => {
const authorization = request.header("authorization")
if (authorization) {
const idToken = authorization.split('Bearer ')[1]
if (!idToken) {
response.status(400).send({ response: "Unauthenticated request!" })
return
}
return admin.auth().verifyIdToken(idToken)
.then(decodedToken => {
// You can check for your custom claims here as well
response.status(200).send({ response: "Authenticated request!" })
})
.catch(err => {
response.status(400).send({ response: "Unauthenticated request!" })
})
}
response.status(400).send({ response: "Unauthenticated request!" })
})
Keep in mind:
If I'm not mistaken, those tokens are valid for 1 hour, if you are going to store them somewhere, just be aware of this. I've tested locally and it takes around 200~500ms - every time - to get only the id token, which in most cases are not that big of overhead - but is significant.
It's going to be easiest for you to use a callable function, since that lets you:
Automatically send the current user's uid in the request.
Know very easily on the function side if a UID was provided in the request, and refuse service if none was provided.
The flutter plugin is here.
You should be able to do the equivalent work yourself, though, since callable functions are just a wrapper around normal HTTP connections. It's possible for you to get the ID token of the logged in user.
import 'package:firebase_messaging/firebase_messaging.dart';
.
.
.
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
Future<void> initState() {
super.initState();
_firebaseMessaging.getToken().then((token) {
assert(token != null);
print("teken is: " + token);
});
}
Get your token from firebaseAuth and put in a string.
Future<Details> getDetails() async {
String bearer = await FirebaseAuth.instance.currentUser!.getIdToken();
print("Bearer: " + bearer.toString());
String token = "Bearer ${bearer}";
var apiUrl = Uri.parse('Your url here');
final response = await http.get(apiUrl, headers: {
'Authorization' : '${token}'
});
final responseJson = jsonDecode(response.body);
return Details.fromJson(responseJson);
}