Example of Login with Tiktok (web) flow - oauth

I would like a user to login with tikok on the web and get his basic information like:
avarat_url
union_id (uniq user identifier provided by tiktok)
display_name
The Tiktok Login Kit for Web Documentation seems to be missing a full example on how to implement the full sequence of calls. Also some things are not explained at all (like the callback URL). Can someone share their full solution with code example on how to integrate tiktok login onto a webpage.

Heres a full example of the tiktok login for web implementation:
setup a tiktok developer account https://developers.tiktok.com/
create a new app, this will generate a CLIENT_KEY and CLIENT_SECRET.
create 2 backend endpoints that you control, for example:
https://example.com/auth : builds a tiktok URL and redirects the user to that endpoint (where the user will be prompted to login).
https://example.com/authCallback : once the user has finished the login with tiktok flow, tiktok sends an authorizationResponse to this endpoint. The authorizationResponse contains info that you need to fetch the users data.
in section "Platform Info": insert the callback URL and redirect domain. The callback URL being the second of the 2 server endpoints listed above. Tiktok will send the authorizationResponse to that URL once the user successfully loggs in and grants or declines the required permissions. In redirect domain simply add the domain without the exact path.
fill out all info for your app and wait for approval, this can take up to 1-3 days.
once approved, you are ready to implement the full flow, which consists of multiple steps/requests.
(A) send the user from your frontend to your first backend endpoint https://example.com/auth. From there, the user will be redirected to the tiktok auth page.
(B) once the user finished the authorization, tiktok sends a authorizationResponse to your callback URL (https://example.com/authCallback), which contains a variable code. With the code you can request the access_token and open_id of the user.
(C) use the access_token and open_id to request basic user info.
(A) Send User to Tiktok Authentication Page
In your frontend, redirect the user to https://example.com/auth. Then run the following nodejs backend code on your /auth route. For this example we use an express app (req = request object, res = response object):
// IMPORTANT, it is your responsibility to store a csrf token
// in your database, to be able to prevent xss attacks, read more
// here (section 2.1) => https://developers.tiktok.com/doc/login-kit-web
const createCsrfState = () => Math.random().toString(36).substring(7);
const csrfState = createCsrfState();
res.cookie('csrfState', csrfState, { maxAge: 60000 });
let url = 'https://open-api.tiktok.com/platform/oauth/connect/';
url += `?client_key=${CLIENT_KEY}`;
url += '&scope=user.info.basic';
url += '&response_type=code';
url += `&redirect_uri=${encodeURIComponent('https://example.com/authCallback')}`;
url += '&state=' + csrfState;
// redirect the user to the generated URL
// user will be prompted to login with tiktok
// and authorize needed permissions
res.redirect(url);
This code redirects the user to a tiktok url, where the user is prompted to sign in with tiktok and grant access.
(B) Handle authorizationResponse, use code to get access_token and open_id
Once the user finished the login process, tiktok sends an authorizationResponse to your second backend server endpoint https://example.com/authCallback. In that callback you recieve variables state and code.
// express example with
// `req` = request object
// `res` = response object
// check if the csrf token is valid
// its the developers responsibility
// to setup a validation logic.
if (!validateCsrfToken(req.query.state)) {
throw new Error("invalid csrf token");
}
async function getAccessTokenAndOpenId(code, TIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET) {
let urlAccessToken = 'https://open-api.tiktok.com/oauth/access_token/';
urlAccessToken += '?client_key=' + TIKTOK_CLIENT_KEY;
urlAccessToken += '&client_secret=' + TIKTOK_CLIENT_SECRET;
urlAccessToken += '&code=' + code;
urlAccessToken += '&grant_type=authorization_code';
const resp = await axios.post(urlAccessToken);
return {
accessToken: resp.data.data.access_token,
openId: resp.data.data.open_id,
};
}
const code = req.query.code;
const { openId, accessToken } = await getAccessTokenAndOpenId(code, TIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET);
(C) Get basic user info
async function getBasicInfo(accessToken, openId) {
let urlBasicInfo = `https://open-api.tiktok.com/user/info/`;
const data = {
access_token: accessToken,
open_id: openId,
fields: [
"open_id",
"union_id",
"avatar_url",
"avatar_url_100",
"avatar_url_200",
"avatar_large_url",
"display_name",
],
};
const resp = await axios.post(urlBasicInfo, data);
return resp.data.data.user;
}
const userBasicInfo = await getBasicInfo(accessToken, openId);
// 🥳 done!

Related

Send my own JWT when using social login (ex. google oauth2)

I have an authentication server where an user can login (user/password) and it sends back a token so the user can access a private api.
I want to implement social login (e.g. google oauth2) but the redirect flow of these logins prevent me to send back a token.
How can I achieve that ?
If understand you correct you want pass your custom data (token) to OAuth2 login process. If so you have state field in url for it. This field prevented during redirects and sent back to you result page.
Pice of sample code in python
state = any_data_base64_coded_or_references
flow = flow_from_clientsecrets(....)
next_page = str( flow.step1_get_authorize_url() + '&state=' + state );
But be carefull url can ba cutted in 255 bytes. So better pass references to your internal entry

Caching Google JWT

I have a mobile app communicates with a server. For authentication in the mobile app, I'm using sign in with google. The sign in returns an accessToken which I send to my server and verify using google-auth-library as suggested here: https://developers.google.com/identity/sign-in/web/backend-auth
import GoogleAuth from 'google-auth-library'
const auth = new GoogleAuth()
const client = new auth.OAuth2(MyClientId, '', '')
apiRoutes.use((req, res, next) => {
// get the token from the request
const token = req.token
if (token) {
// verify secret with google
client.verifyIdToken(token, MyClientId, (err, payload) =>
// proceed with the user authenticated
...
Is it necessary to make this call with every request that the user makes? Would it be good practice to do some sort of caching? Or to have my own implementation of JWT on my server that includes the google payload?
No, the server should usually creates an account for the user once it validates the access token, saving the Google ID in the database along other user details (ID, email, name etc), and then returns an access token to the mobile application.
Once the latter (usually stored locally) expires, it can be refreshed without prompting the user for permission.

How to apply SSO with an existing login screen and a trusted second website

We use IdentityServer3 (IdSvr3) for authorization/authentication. We want to offer the ability for our end user (or resource owner: RO) to log in (log through) to a second trusted website (site B) without login in to site B also, after they have already logged in to an initial website (site A). Site B administers a different set of resources for the RO. It is important that the RO is not redirected to the IdSvr3 login/consent screen. A possible approach I found so far is: inside site A an access token is created by calling RequestResourceOwnerPasswordAsync (passing username and password plus scope = "openid ..."). This access token is send to site B with which the RO can be authenticated. Site B retrieves the user info by calling the connect/userinfo endpoint. I want to know if this is a correct approach/flow. We assume that the RO will always enter site A first, not site B.
Thanks in advance for taking your time to think with me about this.
what you can do here is to send a authorize request to identity server for Site B Scope and request id_token or reference token. Make sure while sending the authorize request to idsrv you are using prompt=none, this way you will get the access_token without showing a consent to the user again if the user is already logged-in to site A.
Below example is doing the same from a JS file. In Site A you can refer to this script file and execute the script using IIFE.
function getIdentityServerURL() {
var url = global.appSettings.identityServerURL
+ "/connect/authorize?client_id=siteB&response_type=id_token token&redirect_uri="
+ global.appSettings.siteBUrl + "/Main/SsoCallback&scope=siteBscope openid email roles&prompt=none&nonce="
+ genNonce();
return encodeURI(url);
}
The code above will redirect you to SsoCallback page where you can create a virtual iframe and post the token back to site B after reducing the result from authorize request. Refer to code below.
<script type="text/javascript">
var identityresult = window.location.hash.split('&').reduce(function (result, item) {
var parts = item.split('=');
result[parts[0]] = parts[1];
return result;
}, {});
window.parent.postMessage(identityresult, '*');
your script can listen to postmessage event. Hope this helps.

Instagram API returning error when requesting access_token on Windows Phone

I've been trying to integrate the Instagram API in my app, but am stuck with the authentication. I had it working completely fine when I was just using the implicit flow version which gave me the access_token as part of the URI fragment.
However, now I'm changing to the server-side flow, in which I receive a code after the user logs in. I then post this code to the access token URL, which will then give me the access_token as well as certain information about the user, such as their username and profile picture link.
I am using the InstaSharp library, modifying the source code.
HttpClient client = new HttpClient { BaseAddress = new Uri(config.OAuthUri + "access_token/", UriKind.Absolute) };
var request = new HttpRequestMessage(HttpMethod.Post, client.BaseAddress);
request.AddParameter("client_secret", config.ClientSecret);
request.AddParameter("client_id", config.ClientId);
request.AddParameter("grant_type", "authorization_code");
request.AddParameter("redirect_uri", config.RedirectUri);
request.AddParameter("code", code);
return client.ExecuteAsync<OAuthResponse>(request);
After creating my request, it is formatted as so:
{Method: POST, RequestUri: 'https://api.instagram.com/oauth/access_token/?client_secret={CLIENT_SECRET}&client_id={CLIENT_ID}&grant_type=authorization_code&redirect_uri=http://instagram.com &code={CODE}', Version: 1.1, Content: , Headers: { }}
(I inserted the space between the redirect_uri and code because it wouldn't let me post the question otherwise)
Everything appears normal in the address, but I always receive an error in the retuned json file:
"{"code": 400, "error_type": "OAuthException", "error_message": "You must provide a client_id"}"
I have no clue what is causing this error. Any help is greatly appreciated!
Thanks!
Elliott
Are you using the latest version of InstaSharp? Fork it here. You can check the README.md there although it's a bit outdated and you need to tweak some config. Here's how you can do it with the latest version that is in github:
// create the configuration in a place where it's more appropriate in your app
InstaSharpConfig = new InstagramConfig(
apiURI, oauthURI, clientId, clientSecret, redirectUri);
// then here's a sample method you can have to initiate auth
// and catch the redirect from Instagram
public ActionResult instagramauth(string code)
{
if (string.IsNullOrWhiteSpace(code))
{
var scopes = new List<InstaSharp.Auth.Scope>();
scopes.Add(InstaSharp.Auth.Scope.likes);
var link = InstaSharp.Auth.AuthLink(
oauthURI, clientId, redirectUri, scopes);
// where:
// oauthURI is https://api.instagram.com/oauth
// clientId is in your Instagram account
// redirectUri is the one you set in your Instagram account;
// for ex: http://yourdomain.com/instagramauth
return Redirect(link);
}
// add this code to the auth object
var auth = new InstaSharp.Auth(InstaSharpConfig);
// now we have to call back to instagram and include the code they gave us
// along with our client secret
var oauthResponse = auth.RequestToken(code);
// save oauthResponse in session or database, whatever suits your case
// oauthResponse contains the field Access_Token (self-explanatory),
// and "User" that'll give you the user's full name, id,
// profile pic and username
return RedirectToAction("action", "controller");
}
Take note that you can split up the "instagramauth" method. Did it that way for brevity.

OAuts with Signpost - how to sign POST to get Access token

I'm working with client who provided me with somewhat vague
instructions. Here's what I'm doing (using CommonsHttpOAuthConsumer as
consumer and DefaultOAuthProvider as provider)
I'm able to get response token from doing this:
String requestToken = provider.retrieveRequestToken
(OAuth.OUT_OF_BAND);
this is in form of URL with params so I'm parsing the actual token
out for example:
https://foobar.com/oauth/login_authorize?oauth_token=XRFCGPbES3M2bYZy...
Now - the instructions that I get say:
Given the request token obtained in step 1, login with the user’s
credentials (name and password) as POST parameters and sign the
request with the request token/secret
POST https://foobar.com/oauth/login_authorize
That's where I'm having difficulties. Obviously I have to input that
requestToken somewhere so I do this (post is HttpPost that contains user credentials):
consumer.setTokenWithSecret(requestToken, SECRET);
consumer.sign(post);
It doesn't work. It actually generates 200 status but what I get is a
generic error message.
retrieveRequestToken does not return a request token, it returns an authenticationUrl that you need to send your users to so they can sign in. Request token is saved in provider object.
String authenticationUrl = provider.retrieveRequestToken( call_back_url )
Note: According to the oauth standard the users sign in on the providers site with their credentials. After they have done that you (as a consumer) can get the Access Token and after that you can access their data on the providers site.
// After user has signed in
provider.retrieveAccessToken(null)
// access token is saved in provider and provider knows which consumer uses it
// so now you can sign with your consumer and connect with the request
URL url = new URL( protected_resources_url )
HttpURLConnection request = (HttpURLConnection) url.openConnection();
consumer.sign(request)
request.connect()
If you have the user credentials you can do the authorization in your script
// Using grails and functional-tests
get(authenticationUrl)
// Image the site shows a simple form with username/password and a login button
setRedirectEnabled false
form {
username = "mario"
password = "peach"
click "login"
}
And then do retrieveRequestToken and the code mentioned above
Hope this helps you
// Jonas

Resources