Bigcommerce - How to request Authorization Code/Access Token - oauth-2.0

In my application, the user when installs the app, needs to fill a registration form. I need to save the access_token along with the user instance.
So, if the user is unregistered, I redirect to the signup form ie. I dont save the access_token, but at this time, the app is registered. Which means, suppose when the store admin logs back in to the app, he does not get the auth code again, but gets signed_payload.
Since, I dont want to store, unregistered users on my database, I prefer calling a api, that would grant me auth code and/or access_token.
Is there any such call I can make?

To answer your question, the access token can only be obtained at the point of the initial app install, when the user installs the app for the very first time. This is the only time that BigCommerce will send the information required to obtain the access token.
Therefore your app should always save the access_token at the point of install. Your registration page should be prompted after obtaining and saving the access token. If for some reason the user installs the app and does not complete the registration, then you should simply just check on your end if the registration was finished or not, and if it wasn't then you should display it during the app load phase as a requirement before displaying your main app dashboard.

Since you didn't specify a programming language, I'm going to illustrate one in Python.
There are two parts you mentioned, registration/access token and signed payload.
The initial callback flow would look something like this:
#app.route('/bigcommerce/callback')
def auth_callback():
# Put together params for token request
code = flask.request.args['code']
context = flask.request.args['context']
scope = flask.request.args['scope']
store_hash = context.split('/')[1]
redirect = app.config['APP_URL'] + flask.url_for('auth_callback')
# Fetch a permanent oauth token. This will throw an exception on error,
# which will get caught by our error handler above.
client = BigcommerceApi(client_id=client_id(), store_hash=store_hash)
token = client.oauth_fetch_token(client_secret(), code, context, scope, redirect)
bc_user_id = token['user']['id']
email = token['user']['email']
access_token = token['access_token']
The flow using a signed payload would look something like:
#app.route('/bigcommerce/load')
def load():
# Decode and verify payload
payload = flask.request.args['signed_payload']
user_data = BigcommerceApi.oauth_verify_payload(payload, client_secret())
if user_data is False:
return "Payload verification failed!", 401
bc_user_id = user_data['user']['id']
email = user_data['user']['email']
store_hash = user_data['store_hash']
When initially creating a user in your database, you can also denote the sign up date through a function of your code and then do a periodic cron job to check if they have a registered account with you. There's not an endpoint where we store whether they completed registration with you since that is a function of your app.

Related

GetStream iOS native - How to update Client token after first initialization

I'm looking for an API to update the Client.Config.token value for the GetStream Library on iOS. Seems like this token is the only way for the library to parse the user_id from the JWT, however it is only parsed at init time of the singleton, Client.shared.
What happens if we need to update the token if the user has logged out of one account and into a different account?
Thank you for your question. For now, the shared Client doesn't support Token update, but you can use a not shared instance of the Client. So, when another user logged in you can create a new Client instance with another token instead of the existing one.
To finish an instance client setup you need to create the current user like this:
if let currentUserId = client.currentUserId {
client.create(user: User(id: currentUserId)) { [weak client] result in
client?.currentUser = try? result.get()
}
}
We'll add a token update for the shared Client in future releases.

New "OAuth2 Authorization code was already redeemed" error when getting an access code for an Office 365 resource

We have the following steps to authenticate with Office 365 and get the access tokens required to request calendar events...
Send the user to https://login.microsoftonline.com/common/oauth2/authorize?response_type=code , passing the client id and a redirect URL
The user logs in and is redirected back to the redirect URL with a ?code= query string value
Make an API request to https://login.microsoftonline.com/common/oauth2/token to get a token for the https://api.office.com/discovery/ resource, passing the code that we got in step 2
Store the access token and refresh token which is returned
Make an API request to https://api.office.com/discovery/v2.0/me/services to get a list of end points that we have access to
For the end point that I want to work with (Office 365 Exchange) , make an API request to https://login.microsoftonline.com/common/oauth2/token to get a token for the https://outlook.office365.com/ resource, passing the code that we got in step 2
Store the access token and refresh token which is returned
Use the access token from step 7 to fetch calendar events
This used to work fine, but today I'm getting a different result from the API call on step 6
I used to get an access token and refresh token which I could use for the outlook.office365.com resource, but now I'm getting an error message like the following...
{
"error":"invalid_grant",
"error_description":"AADSTS70002: Error validating credentials. AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.
Trace ID: 37f11c2c-6450-4040-b297-48268c6d4b00
Correlation ID: afdeb0cb-c14a-4267-9a5d-2422a1f84d62
Timestamp: 2019-01-29 22:26:24Z",
"error_codes":[70002,54005],
"timestamp":"2019-01-29 22:26:24Z",
"trace_id":"37f11c2c-6450-4040-b297-48268c6d4b00",
"correlation_id":"afdeb0cb-c14a-4267-9a5d-2422a1f84d62"
}
So it looks like I need to pass a different code the second time I make a request to get access tokens, but where do I get this different code from?

Google OAuth 2 access token not working as expected some hours after authorizing

I'm using the Google Identity Platform's OAuth 2.0 flow to authorize a javascript/HTML teacher observation form to write to a Google Sheets document. Everything is working well most of the time; however, last night one of our principals hit the following error:
"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project."
I determined that he had launched the observation tool in the afternoon, and now maybe five hours later was trying to click the submit button. My hunch was that the token had expired, but from Google's documentation it seems like the JS auth library is meant to handle refreshing the access token as necessary - I believe it's not actually possible to get a refresh token to do anything manually.
I'm using what is essentially the sample auth code, and the app responds to being signed out appropriately. That is, if I sign out in another tab, the submit button is disabled and the sign-in button appears again. Assuming token expiration is the issue here, any ideas on the correct way to identify if the token has expired and how to request a new one, ideally without user interaction? Or if it's not an expiration issue, what else could it be? This user has successfully submitted data in earlier observations; it was just this one time when he waited ~5 hours (potentially losing internet connectivity / sleeping his laptop) during that time.
Here's the auth code:
var clientId = ""; //id removed
var discoveryDocs = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];
var scopes = "https://www.googleapis.com/auth/spreadsheets";
var authorizeButton = document.getElementById('authorize-button');
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
gapi.client.init({
discoveryDocs: discoveryDocs,
clientId: clientId,
scope: scopes
}).then(function () {
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
});
}
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = 'none';
document.getElementById('submit').disabled = false;
findRow(); //find the empty row once we're logged in
} else {
authorizeButton.style.display = 'block';
document.getElementById('submit').disabled = true;
}
}
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
Thank you!
Similar issues that i had resulted in issues from that Authorized Javascript origins.
"In the Authorized JavaScript origins field, enter the origin for your app. You can enter multiple origins to allow for your app to run on different protocols, domains, or subdomains. You cannot use wildcards. In the example below, the second URL could be a production URL." taken from https://developers.google.com/identity/sign-in/web/devconsole-project.If prompt to view task came from an email, the email origin must be verified -or- the device is used for multiple accounts, the token will not stay. If the api is being improperly used, it will allow functionality for a short period of time , then fail.
This may be useful, in the authflow, you do not have scope or id in options
/** * Initiate auth flow in response to user clicking authorize button. * *
#param {Event} event Button click event. */ function
handleAuthClick(event) {
gapi.auth.authorize( {client_id: '[#app:client_id]', scope:
["googleapis.com/auth/calendar"], immediate: false}, handleAuthResult);
return false; }
I believe How to refresh expired google sign-in logins? had the answer I needed. Since all of my API calls happen at once, I added a new Date() when the page loads, a second new Date() when the submission flow begins, and if they are more than 45min (2,700,700ms) apart, I use gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse() to force an access token refresh, as documented at https://developers.google.com/identity/sign-in/web/reference#googleuserreloadauthresponse.
Hopefully Google will eventually update their documentation to reflect this now-necessary step when using the auth2 flow vs the older auth flow.
Time will tell if this actually solved the issue, but I'm hopeful!
I hope it helps you friend that error is because you have the wrong time, you go to date and time settings then press synchronize now.

How to authenticate user with token (stay authenticated in iPhone)

I have two related questions and I hope someone help me because I've been stuck for 2 days
First: mobile phone failed to authenticate
Here is what I have done:
1- user signs up
2- token released
3- token saved in user's device
but then when the same user try to do API requests I get
Rooute to sign up :
$api = app('Dingo\Api\Routing\Router');
$api->version('v1', function ($api) {
$api->post('auth/signup', 'App\Api\V1\Controllers\AuthController#signup');
then I get a token , so I guess everything looks great!
then now when the same device sends a post request to laravel I get this message
"message": "Failed to authenticate because of bad credentials or an invalid authorization header."
this is the route to the post request
$api->group(['middleware'=>'api.auth'],
function ($api) {
$api->post('auth/ios', 'App\Api\V1\Controllers\AuthController#create');
Second: is my method right to save data made by a mobile phone?
Since I couldn't test this method I'd like to know if this is at least one of the right ways to receive data and save it. The reason to save it is because I will show it in a control panel.
public function create(Request $request)
{
$user = new User();
$id = Auth::id();
$user->phone = $request->input('phone');
$user->city = $request->input('city');
$user->street = $request->input('street');
$user->save();
return 'Employee record successfully created with id ' . $user->id;
}
I understand that you are authenticate users based on api token.
Here is what you could do :
set up a column called api_token in users table by adding the following migration
$table->string('api_token', 60)->unique();.This generates a random api token for every user.
send the api_token back to the user's device and save it there
Send it back with every request. Preferalbly set it up globally and send it in the request Authentication request header
Get the authenticated user like so$user= Auth::guard('api')->user();
Laravel takes care of all the authentication stuff behind the scenes.
Learn More about this here

Identifying users in Rails Web push notifications

I'm developing a Rails application, and I'd like to send web push notifications to specific users when certain actions happen, e.g:
A user started tracking a timer, but the timer has been running for more than 6 hours. Then the app sends that user a web notification.
I've been doing research and found this tutorial, the author implements push notifications for Rails, however there's no insight on how to identify the users.
From what I understood, the users needs to subscribe from their browser to be able to get push notifications, however, considering each user can use the application from multiple browsers, how can I automatically subscribe/unsubscribe a user for notifications in all browsers they use the app from?
So, what I did was adding a notification_subscription model to my User model.
On my javascript, I check if there's a current browser subscription present:
this.serviceWorkerReady()
.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager.getSubscription()
.then((pushSubscription) => {
if (pushSubscription && _.includes(subscriptionEndpoints, pushSubscription.endpoint)) {
return;
}
this.subscribe();
});
});
I check if the current subscription is already present in the user stored endpoints, and subscribe if it isn't.
On subscription I send the new endpoint to the backend, which adds the new subscription to the user:
$.post(Routes.users_subscriptions_path({ format: 'json' }), {
subscription: subscription.toJSON()
});
Then I can send notifications to users to every endpoint:
def push_notifications_to_user(user)
message = {
title: "A message!",
tag: 'notification-tag'
}
user.notification_subscriptions.each do |subscription|
begin
Webpush.payload_send(
message: JSON.generate(message),
endpoint: endpoint,
p256dh: p256dh,
auth: auth,
api_key: public_key
)
rescue Webpush::InvalidSubscription => exception
subscription.destroy
end
end
end
The webpush gem raises an InvalidSubscription exception if the endpoint is invalid, we can destroy that endpoint to keep only the valid endpoints from the user.
The endpoint is unique by browser so you need an additional authentication scheme on the top of your app to send user's information along with the new endpoint.
You need to attach metadata (i.e. the user ID) to the endpoint when you store it on your server:
#subscription = Subscriptions.new endpoint: params[:endpoint]
#subscription.user = current_user
// or if you send with AJAX the user id together with the endpoint
#subscription.user = User.find params[:user_id]
In the second case I suggest to sign the user ID or use a secret token, otherwise anyone would be able to subscribe to push notifications as if it was another user.
Then you can delete from the database all the endpoints that belong to that user ID to unsubscribe all his devices.
However I don't think it's a good practice: a user may want to receive notifications on a device and not on another one.

Resources