Blog

Tagged by 'oauth'

  • Don’t you love the good ol’ days back when querying API’s provided by social platforms was a straight forward process and at a time when it felt like there were fewer barriers for entry. This thought came to mind when I had to use Instagram’s API feed again recently to output a list of photos for one of my personal projects.

    The last time I carried out any interaction with Instagram's API was back in 2013 where it was a much more simpler affair. All that was required is to generate an access token that never expired, which could then be used against any API endpoint to get data back from Instagram.

    Reading Jamie Maguires really useful three-part blog post on tapping into Instagrams API gave me a good foundation into how the API has changed since I used it last. But the example he used required the profile you wanted to interact with had to be set up as a business user. However, interacting with Instagram’s API requires a lot more effort if you (like most users) do not have a business profile.

    As far as I understand it, if you plan on making any interaction with Instagrams API as a non-business user the process is:

    1. Create Facebook Developer App
    2. Authenticate application by logging into the Instagram account. This will then generate a short-lived token valid for 1 hour.
    3. Exchange the short-lived token for a long-lived token that is valid for 60 days. This token will need to be stored somewhere at the application level.
    4. Ensure the long-lived token is refreshed before it expires within the 60-day window.

    It is very important that we are using the long-lived token and to continually renew it by having some form of background process that carries out this check, whether this is at application or Azure level. We can then use this token to make queries to Instagram API endpoints.

    In this post, I am going to perform very simple integration to output a list of images from an Instagram profile. By demonstrating this, we should then get a fair idea on how to interact with the other API endpoints. Personally, setting everything up the process to acquire an access-token is the part that requires the most effort. So let's get to it!

    Create a Facebook Developer Application

    Since Facebook has taken over Instagram, naturally the application process starts within Facebook's developer site, which can be found at: https://developers.facebook.com/apps/. Once you have logged in using your Facebook credentials, the following steps will need to be carried out:

    • Select "Add New App". In the popup, enter the application name and contact email.
    • You will be presented with a list of products. Select "Instagram" by pressing the "Setup" button.
    • From the left-hand navigation, go to: Settings > Basic. Scroll to the bottom and click on the "Add platform" button. From the popup, select "Website".
    • Enter the website URL. For testing, this will be the URL we set up in the previous section. Ensure this URL is prefixed with https://. Click the "Save" button.
    • Again, from the left-hand navigation (under Products > Instagram), select: "Basic Display". At the bottom of the page, click the "Create New App" button.
    • Enter the following fields (based on our test domain):
    • Valid OAuth Redirect URIs: https://myinstagramapp.surinderbhomra.com/Instagram/Auth
    • Deauthorize Callback URL: https://myinstagramapp.surinderbhomra.com
    • Data Deletion Requests: https://myinstagramapp.surinderbhomra.com
    • Add an Instagram Test user. This can be the clients Instagram profile name.
    • Add instagram_graph_user_media permission, so we can read the profile images.
    • Click "Save Changes" button.

    You will have noticed I have added website URL’s for the OAuth Redirect, Deauthorize Redirect and Deletion Request fields. As these fields are required, you can enter the dummy website URL for local development purposes. Just remember to change this when you move the application to the live domain. In the meantime to utilise those dummy URL’s, your local host file will need to be updated. Please refer to a post I wrote in 2012 for further information. It might be over 8 years old, but the process is the same even if the Facebook Developer interface has changed.

    Once the Developer Application has been set up, grab the App ID and App Secret to be used in our demo application.

    Photo Feed Application

    The Photo Feed application will provide a very simple demonstration of the authentication process and interacting with the API to get back our media objects. From here, you will have the tools to improve and expand on this to delve deeper into other API endpoints Instagram has to offer.

    To start, we have two helper classes that the application will rely on:

    1. InstagramAuthProvider
    2. InstagramMediaProvider

    InstagramAuthProvider

    The InstagramAuthProvider carries out all authentication processes for acquiring both short and long-lived tokens.

    public class InstagramAuthProvider
    {
        #region Json Response Objects
    
        public class AuthenticateRequest
        {
            [JsonProperty("error_type")]
            public string ErrorType { get; set; }
    
            [JsonProperty("code")]
            public int StatusCode { get; set; }
    
            [JsonProperty("error_message")]
            public string ErrorMessage { get; set; }
    
            [JsonProperty("access_token")]
            public string AccessToken { get; set; }
    
            [JsonProperty("user_id")]
            public long UserId { get; set; }
        }
    
        public class LongLivedTokenRequest
        {
            [JsonProperty("access_token")]
            public string AccessToken { get; set; }
    
            [JsonProperty("token_type")]
            public string TokenType { get; set; }
    
            [JsonProperty("expires_in")]
            public long ExpiresInSeconds { get; set; }
        }
    
        #endregion
    
        /// <summary>
        /// Carries out initial authentication approach after user had approved app to Instagram account link.
        /// Returns a short-lived token valid for 1 hour.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public static async Task<AuthenticateRequest> GetAccessTokenAsync(string code)
        {
            string authResponse = string.Empty;
    
            if (!string.IsNullOrEmpty(code))
            {
                Dictionary<string, string> parameters = new Dictionary<string, string>
                    {
                        { "client_id", ConfigurationManager.AppSettings["Instagram.ClientID"].ToString() },
                        { "client_secret", ConfigurationManager.AppSettings["Instagram.AppSecret"].ToString() },
                        { "grant_type", "authorization_code" },
                        { "redirect_uri", $"{ConfigurationManager.AppSettings[“Site.Domain"]}{ConfigurationManager.AppSettings["Instagram.AuthRedirectPath"]}" },
                        { "code", code }
                    };
    
                FormUrlEncodedContent encodedParameters = new FormUrlEncodedContent(parameters);
    
                HttpClient client = new HttpClient();
    
                HttpResponseMessage response = await client.PostAsync("https://api.instagram.com/oauth/access_token", encodedParameters);
                authResponse = await response.Content.ReadAsStringAsync();
            }
    
            return JsonConvert.DeserializeObject<AuthenticateRequest>(authResponse);
        }
    
        /// <summary>
        /// Exchanges a short-lived token for a long-lived token that are valid for 60 days.
        /// </summary>
        /// <param name="shortliveAccessToken"></param>
        /// <returns></returns>
        public static async Task<LongLivedTokenRequest> GetLongLifeTokenAsync(string shortliveAccessToken)
        {
            string authResponse = string.Empty;
    
            if (!string.IsNullOrEmpty(shortliveAccessToken))
            {
                HttpClient client = new HttpClient();
    
                HttpResponseMessage response = await client.GetAsync($"https://graph.instagram.com/access_token?client_secret={ConfigurationManager.AppSettings["Instagram.AppSecret"].ToString()}&grant_type=ig_exchange_token&access_token={shortliveAccessToken}");
                authResponse = await response.Content.ReadAsStringAsync();
            }
    
            return JsonConvert.DeserializeObject<LongLivedTokenRequest>(authResponse);
        }
    
        /// <summary>
        /// Refresh a long-lived Instagram User Access Token that is at least 24 hours old but has not expired.
        /// </summary>
        /// <param name="longLivedAccessToken"></param>
        /// <returns></returns>
        public static async Task<LongLivedTokenRequest> RefreshTokenAsync(string longLivedAccessToken)
        {
            string authResponse = string.Empty;
    
            if (!string.IsNullOrEmpty(longLivedAccessToken))
            {
                HttpClient client = new HttpClient();
    
                HttpResponseMessage response = await client.GetAsync($"https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token={longLivedAccessToken}");
                authResponse = await response.Content.ReadAsStringAsync();
            }
    
            return JsonConvert.DeserializeObject<LongLivedTokenRequest>(authResponse);
        }
    }
    

    InstagramMediaProvider

    The InstagramMediaProvider returns media information based on the authentication token.

    public class InstagramMediaProvider
    {
        #region Json Response Objects
    
        public class MediaCollection
        {
            [JsonProperty("data")]
            public List<MediaInfo> Data { get; set; }
        }
    
        public class MediaInfo
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("caption")]
            public string Caption { get; set; }
    
            [JsonProperty("permalink")]
            public string InstagramUrl { get; set; }
    
            [JsonProperty("media_type")]
            public string Type { get; set; }
    
            [JsonProperty("thumbnail_url")]
            public string VideoThumbnailUrl { get; set; }
    
            [JsonProperty("media_url")]
            public string Url { get; set; }
        }
    
        #endregion
    
        private string _accessToken;
    
        public InstagramMediaProvider(string accessToken)
        {
            _accessToken = accessToken;
        }
    
        /// <summary>
        /// Gets list of all user images.
        /// </summary>
        /// <returns></returns>
        public async Task<List<MediaInfo>> GetUserMedia()
        {
            var mediaInfo = await GetAllMediaAsync();
    
            if (mediaInfo?.Data.Count > 0)
                return mediaInfo.Data;
            else
                return new List<MediaInfo>();
        }
    
        /// <summary>
        /// Outputs information about a single media item.
        /// </summary>
        /// <returns></returns>
        private async Task<MediaCollection> GetAllMediaAsync()
        {
            string mediaResponse = string.Empty;
    
            HttpClient client = new HttpClient();
    
            HttpResponseMessage response = await client.GetAsync($"https://graph.instagram.com/me/media?fields=id,media_type,media_url,thumbnail_url,permalink,caption,timestamp&access_token={_accessToken}");
    
            mediaResponse = await response.Content.ReadAsStringAsync();
    
            if (response.StatusCode != HttpStatusCode.OK)
                return null;
    
            return JsonConvert.DeserializeObject<MediaCollection>(mediaResponse);
        }
    }
    

    Authorisation and Authentication

    The authorisation and authentication functionality will be performed in the InstagramController.

    public class InstagramController : Controller
    {
         /// <summary>
         /// Authorises application with Instagram.
         /// </summary>
         /// <returns></returns>
        public ActionResult Authorise()
        {
            return Redirect($"https://www.instagram.com/oauth/authorize?client_id={ConfigurationManager.AppSettings["Instagram.AppID"].ToString()}&redirect_uri={ConfigurationManager.AppSettings[“Site.Domain"]}{ConfigurationManager.AppSettings["Instagram.AuthRedirectPath"]}&scope=user_profile,user_media&response_type=code");
        }
    
        /// <summary>
        /// Makes authentication request to create access token.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<ActionResult> Auth(string code)
        {
            InstagramAuthProvider.AuthenticateRequest instaAuth = await InstagramAuthProvider.GetAccessTokenAsync(code);
    
            if (!string.IsNullOrEmpty(instaAuth?.AccessToken))
            {
                InstagramAuthProvider.LongLivedTokenRequest longTokenRequest = await InstagramAuthProvider.GetLongLifeTokenAsync(instaAuth.AccessToken);
    
                if (!string.IsNullOrEmpty(longTokenRequest?.AccessToken))
                {                
                   // Storing long-live token in a session for demo purposes. 
                   // Store the token in a more permanent place, such as a database.
                    Session["InstagramAccessToken"] = longTokenRequest.AccessToken;
    
                    return Redirect("/");
                }
            }
    
            return Content("Error authenticating.");
        }
    }
    

    Authorise

    Before we can get any of our tokens, the first step is to get authorisation from Instagram against our web application. So somewhere in the application (preferably not publicly visible), we will need an area that will kick this off. In this case, by navigating to /Instagram/Authorise, will cause the Authorise action in the controller to be fired.

    All the Authorise action does is takes you to Instagrams login page and sends over the App ID and Redirect Path. Remember, the Redirect path needs to be exactly as you've set it in your Facebook Developer Application. Once you have successfully logged in, you’ll be redirected back to the application.

    NOTE: I’ll be honest here and say I am not sure if there is a better way to acquire the access token as it seems very odd to me that you have to log in to Instagram first. If any of you know of a better way, please leave a comment.

    Auth

    If the authorise process was successful, you will be redirected back the application and be given an authorisation code. The authorisation code will be parsed to the InstagramAuthProvider.GetAccessTokenAsync() method to be exchanged for our first access-token valid - short-lived valid for 1 hour.

    The last step of the process is to now send the short-lived access token to the InstagramAuthProvider.GetLongLifeTokenAsync() method that will carry out a final exchange to retrieve our long-life token valid for 60 days. It is this token we need to store somewhere so we can use it against any Instagram API endpoint.

    In my example, the long-life token is stored in a Session for demonstration purposes. In a real-world application, we would want to store this in a database somewhere and have a scheduled task in place that will call the InstagramAuthProvider.RefreshTokenAsync() method every 59 days for the long life token to be renewed for another 60 days.

    Photo Feed

    Now we come onto the easy part - use the long-live access token to return a list of photos.

    public class InstagramMediaController : Controller
    {
        /// <summary>
        /// Outputs all user images from Instagram profile.
        /// </summary>
        /// <returns></returns>
        [OutputCache(Duration = 60)]
        public PartialViewResult PhotoGallery()
        {
            if (Session["InstagramAccessToken"] != null)
            {
                InstagramMediaProvider instaMedia = new InstagramMediaProvider(Session["InstagramAccessToken"].ToString());
    
                return PartialView("_PhotoGallery", Task.Run(() => instaMedia.GetUserMedia()).Result);
            }
    
            return PartialView("_PhotoGallery", new List<InstagramMediaProvider.MediaInfo>());
        }
    }
    

    This controller contains a single piece of functionality - a PhotoGallery partial view. The PhotoGallery partial view uses the InstagramMediaProvider.GetUserMedia() method to return a collection of profile photos.

    Final Thoughts

    Before carrying out any Instagram integration, think about what you are trying to achieve. Look through the documentation to ensure the API's you require are available as some permissions and features may require business or individual verification to access live data.

    Also, you may (or may not) have noticed whilst going through the Facebook Development Application setup that I never submitted the application for review. I would advise you to always submit your application to Facebook as you might find your access is revoked. For personal purposes where you're only outputting your own Instagram profile information, you could just leave it "In development" mode. But again, I do not advise this, especially when working with Business accounts.

  • I've written some code that outputs images using Instagram's Developer API. The code can either output images based on a user's profile or via search term.

    As you may already know, in order to get any form of information from any external API an access token is required. Before we dive into some code, the first thing that we need to do is register ourselves as an Instagram Developer by going to: http://instagram.com/developer/.

    Next, we need to register a new client specifically for our intended use. In my case, all I want to do is to get all image information from my own Instagram profile.

    Instagram API - Register New Client

    Here, you will be supplied with Client ID and Client Secret codes. But most importantly, you will need to set an OAuth Redirect URL (or Callback URL) for user's to authenticate your application.

    The strange thing I've noticed about the Instagram API is that a callback page is a compulsory requirement. Even if you are planning on carrying out something as simple as listing some images from your own profile where a public users intervention is not required.

    I'm not interested in their images, I'm interested in my own. I hope Instagram changes this soon. If Twitter can allow you to retrieve tweets by simply registering your application, why can't Instagram?

    From what I've read on Instagram's Google Group's is that an access token needs to only be generated once and they don't expire. But of course Instagram have stated:

    "These tokens are unique to a user and should be stored securely. Access tokens may expire at any time in the future."

    Just make sure you have some fail safe's in your code that carries out the re-authentication process within your application on the event access token has expired. In my own implementation, I've kept the callback page secret and new access token requests can be made within an Administration interface.

    So lets get to the code.

    Step 1: Authentication Request Classes

    These strongly-typed classes mirror the exact structure of the JSON returned from our authentication request. Even though we only require the "access_token" property, I've added additional information, such as details on the Instagram user making the request.

    public class AuthToken
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }
    
        [JsonProperty("user")]
        public InstagramUser User { get; set; }
    }
    
    public class InstagramUser
    {
        [JsonProperty("id")]
        public string ID { get; set; }
    
        [JsonProperty("username")]
        public string Username { get; set; }
    
        [JsonProperty("full_name")]
        public string FullName { get; set; }
    
        [JsonProperty("profile_picture")]
        public string ProfilePicture { get; set; }
    }
    

    It's worth noting at this point that I'm using Newtonsoft.Json framework.

    Step 2: Callback Page

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!String.IsNullOrEmpty(Request["code"]) && !Page.IsPostBack)
        {
            try
            {
                string code = Request["code"].ToString();
    
                NameValueCollection parameters = new NameValueCollection();
                parameters.Add("client_id", ConfigurationManager.AppSettings["instagram.clientid"].ToString());
                parameters.Add("client_secret", ConfigurationManager.AppSettings["instagram.clientsecret"].ToString());
                parameters.Add("grant_type", "authorization_code");
                parameters.Add("redirect_uri", ConfigurationManager.AppSettings["instagram.redirecturi"].ToString());
                parameters.Add("code", code);
    
                WebClient client = new WebClient();
                var result = client.UploadValues("https://api.instagram.com/oauth/access_token", "POST", parameters);
    
                var response = System.Text.Encoding.Default.GetString(result);
    
                var jsResult = JsonConvert.DeserializeObject(response);
    
                //Store Access token in database
                InstagramAPI.StoreAccessToken(jsResult.AccessToken);
    
                Response.Redirect("/CallbackSummary.aspx?status=success", false);
            }
            catch (Exception ex)
            {  
                EventLogProvider.LogException("Instagram - Generate Authentication Key", "INSTAGRAM", ex);
    
                Response.Redirect("/CallbackSummary.aspx?status=error");
            }
        }
    }
    

    As you can see, I'm redirecting the user to a "CallbackSummary" page to show if the authentication request was either a success or failure. (Remember, the page is secured within my own Administration interface.)

    If the request is successful, the access token is stored.

    Step 3: Request Callback Page

    The last piece of the puzzle is to actually request our callback page by authorizing ourselves via Instagram API. In this case, I just have a simple page with the following mark up:

    <p>If Instagram fails to output images to the page, this maybe because a new Authorisation key needs to be generated.</p>
    <p>To generate a new key, press the button below and follow the required steps.</p>
    <a onclick="window.open('https://api.instagram.com/oauth/authorize/?client_id=<%=ConfigurationManager.AppSettings["instagram.clientid"].ToString() %>&redirect_uri=<%=ConfigurationManager.AppSettings["instagram.redirecturi"].ToString() %>&response_type=code', 'newwindow', config='height=476,width=641,toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no,directories=no, status=no'); return false;" href="#" target="_parent">Generate</a>
    

    If all goes to plan, you should have successfully recieved the access token.

    I will post more Instagram code in future posts.

  • Ever since Twitter ditched version 1 of their API to version 1.1, an additional hurdle created when attempting to get any data from Twitter. Authentication (using OAuth) is now required on all API request endpoints. I can see why Twitter decided to go down this route but it does add a little headache when carrying out the most simplest requests.

    In all the sites I have worked on, I've always relied on third party libraries to help me interact with Twitter, such as Twitterizer (no v1.1 support) and TweetSharp. But due to the increased complexity on some of the sites I've been working on, third party libraries don't seem to cut the mustard any more. A more scalable solution is required...

    I came across a really simple and expandable class library called OAuthTwitterWrapper that stemmed from a StackOverflow question I found. Out-of-the-box, this library contains calls required to retrieve a user's timeline and return searches which is great to get you up and running quickly.

    The OAuthTwitterWrapper provided me a really good basis to add further Twitter features, for example a list of users favourite tweets.

    If you don't plan on doing anything too complex when interacting with the Twitter API, third party libraries such as TweetSharp will meet your everyday needs. However, if you want more control over how you make requests, the OAuthTwitterWrapper provides a good foundation.