다음을 통해 공유


Azure Mobile App Service - Get personal info of authenticated users

Introduction

Some days ago, I was searching for an Azure Mobile Apps topic to write about, which I love, and I could barely find a blog post on the internet about how to get the user information like his name, his profile picture or even his friends (for more complex scenarios). So, I am gonna show you how you can easily fetch the personal info of an authenticated user of your UWP (and not only) application from Facebook, Google, Microsoft Account or Twitter.
Are you ready to create a great user experience for your users?

 

The result of this tutorial will be like:

https://uwpstories.files.wordpress.com/2016/04/capture1.png?w=544

I am gonna focus on the backend so I 'll keep the UI of the UWP App pretty simple & ugly.

 

You can find the complete project here. (GitHub)

Create a new Azure Mobile App backend

Follow these steps to create a new Mobile App backend.

  1. Log into the Azure Portal.
  2. In the top left of the window, click the +NEW button > Web + Mobile > Mobile App, then provide a name for your Mobile App backend.
  3. In the Resource Group box, select an existing resource group. If you have no resource groups, enter the same name as your app.At this point, the default App Service plan is selected, which is in the Standard tier. The App Service plan settings determine the location, features, cost and compute resources associated with your app. You can either select another App Service plan or create a new one. For more about App Services plans and how to create a new plan in a different pricing tier and in your desired location, see Azure App Service plans in-depth overview
  4. Use the default App Service plan, select a different plan or create a new plan, then click Create.This creates the Mobile App backend. Later you will deploy your server project to this backend. Provisioning a Mobile App backend can take several minutes; the Settings blade for the Mobile App backend is displayed when complete. Before you can use the Mobile App backend, you must also define a connection a data store.

(For this tutorial we are not going to use the database but you must define a connection to one in order to create the .NET Backend successfully.)

Configure the server project

  1. Back in the Mobile App backend settings, click Quick start > your client platform.
  2. Under Create a table API, select C#:

Click Download, extract the compressed project files to your local computer, open the solution in Visual Studio, build the project to restore the NuGet packages, then deploy the project to Azure. To learn how to deploy a .NET backend server project to Azure, see How to: Publish the server project in the .NET backend SDK topic.

You Mobile App backend is now ready to use with your client app.

(Taken from here)

Configure your Backend for Authentication

Now we have to configure our backend for authentication. There are some great guides for this on Azure Documentation.

Facebook

Please click here for Facebook Authentication configuration

Google

Please click here for Google Authentication configuration

Microsoft Account

Please click here for Microsoft Account Authentication configuration

Twitter

Please click here for Twitter Authentication configuration

 

Code code code!

Backend

We are ready to write some code for our backend!

https://uwpstories.files.wordpress.com/2016/04/capture.png

*That's how a plain and clean Azure Mobile App project, downloaded from the Quick Start, is like.

*

Let's create a new Controller.

  1. Right click on the Controllers folder
  2. Go to Add ->  Controller...
  3. Select Azure Mobile Apps Custom Controller
  4. Click the Add button
  5. Name it whatever you like ( I named mine UserInfoController.cs )
  6. Click Add

 

Azure Mobile Apps Custom Controller are Web API 2 REST-ful Controllers which are specialized in returning data to the client-consumer. They take care of transparently serializing the data into the format requested by the client. In our example, we will use it to get the user data, like his name and his profile picture, from the provider, like Microsoft, and return it to the client.

Now, as we created our Controller lets create our model class which will be returned to the user when our Controller is being invoked.

  1. So, right click on the DataObjects folder
  2. Add a new class.
  3. And name it whatever you like ( I named mine UserInfo.cs )

 

Place the following 2 properties inside our new class.

public class  UserInfo
{
    public string  Name { get; set; }
    public string  ImageUri { get; set; }
 
}

Open your Controller class and add the following code

[MobileAppController]
public class  UserInfoController : ApiController
{
    //Your Twitter Consumer credentials here. Twitter uses OAuth 1.1 so they are necessary.
    const string  TwitterConsumerKey = "<YOUR TWITTER CONSUMER KEY HERE>";
    const string  TwitterConsumerSecret = "<YOUR TWITTER CONSUMER SECRET HERE>";
 
    /// <summary>
    /// Returns the caller's info from the correct provider. The user who invokes it must be authenticated.
    /// </summary>
    /// <returns>The users info</returns>
    public async Task<UserInfo> GetUserInfo()
    {
 
        string provider = "";
        string secret;
        string accessToken = GetAccessToken(out provider, out secret);
 
        UserInfo info = new  UserInfo();
        switch (provider)
        {
            case "facebook":
                using (HttpClient client = new HttpClient())
                {
                    using (
                        HttpResponseMessage response =
                            await
                                client.GetAsync("https://graph.facebook.com/me" + "?access_token=" +
                                                accessToken))
                    {
                        var o = JObject.Parse(await response.Content.ReadAsStringAsync());
                        info.Name = o["name"].ToString();
                    }
                    using (
                        HttpResponseMessage response =
                            await
                                client.GetAsync("https://graph.facebook.com/me" +
                                                "/picture?redirect=false&access_token=" + accessToken))
                    {
                        var x = JObject.Parse(await response.Content.ReadAsStringAsync());
                        info.ImageUri = (x["data"]["url"].ToString());
                    }
                }
                break;
            case "google":
                using (HttpClient client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Authorization =
                        AuthenticationHeaderValue.Parse("Bearer " + accessToken);
                    using (
                        HttpResponseMessage response =
                            await
                                client.GetAsync("https://www.googleapis.com/oauth2/v2/userinfo"))
                    {
                        var o = JObject.Parse(await response.Content.ReadAsStringAsync());
                        info.Name = o["name"].ToString();
                        info.ImageUri = (o["picture"].ToString());
                    }
                }
                break;
            case "twitter":
 
                //generating signature as of https://dev.twitter.com/oauth/overview/creating-signatures
                string nonce = GenerateNonce();
                string s = "oauth_consumer_key=" + TwitterConsumerKey + "&oauth_nonce=" +
                           nonce +
                           "&oauth_signature_method=HMAC-SHA1&oauth_timestamp=" +
                           DateTimeToUnixTimestamp(DateTime.Now) + "&oauth_token="  + accessToken +
                           "&oauth_version=1.0";
                string sign = "GET"  + "&" +
                              Uri.EscapeDataString("https://api.twitter.com/1.1/account/verify_credentials.json") +
                              "&" + Uri.EscapeDataString(s);
                string sec = Uri.EscapeDataString(TwitterConsumerSecret) +  "&" + Uri.EscapeDataString(secret);
                byte[] key = Encoding.ASCII.GetBytes(sec);
                string signature = Uri.EscapeDataString(Encode(sign, key));
 
                using (HttpClient client = new HttpClient())
                {
 
                    client.DefaultRequestHeaders.Authorization =
                        AuthenticationHeaderValue.Parse("OAuth oauth_consumer_key =\"" + TwitterConsumerKey +
                                                        "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"" +
                                                        DateTimeToUnixTimestamp(DateTime.Now) + "\",oauth_nonce=\""  +
                                                        nonce +
                                                        "\",oauth_version=\"1.0\",oauth_token=\"" + accessToken +
                                                        "\",oauth_signature =\"" + signature + "\"");
                    using (
                        HttpResponseMessage response =
                            await
                                client.GetAsync("https://api.twitter.com/1.1/account/verify_credentials.json"))
                    {
                        var o = JObject.Parse(await response.Content.ReadAsStringAsync());
                        info.Name = o["name"].ToString();
                        info.ImageUri = (o["profile_image_url"].ToString());
                    }
                }
                break;
            case "microsoftaccount":
                using (HttpClient client = new HttpClient())
                {
                    using (
                        HttpResponseMessage response =
                            await
                                client.GetAsync("https://apis.live.net/v5.0/me" + "?access_token=" +
                                                accessToken))
                    {
                        var o = JObject.Parse(await response.Content.ReadAsStringAsync());
                        info.Name = o["name"].ToString();
                    }
                }
                using (HttpClient client = new HttpClient())
                {
                    using (
                        HttpResponseMessage response =
                            await
                                client.GetAsync("https://apis.live.net/v5.0/me" +
                                                "/picture?suppress_redirects=true&type=medium&access_token=" +
                                                accessToken))
                    {
                        var o = JObject.Parse(await response.Content.ReadAsStringAsync());
                        info.ImageUri = o["location"].ToString();
                    }
                }
                break;
        }
 
        return info;
    }
 
    /// <summary>
    /// Returns the access token and the provider the current user is using.
    /// </summary>
    /// <param name="provider">The provider e.g. facebook</param>
    /// <param name="secret">The user's secret when using Twitter</param>
    /// <returns>The Access Token</returns>
    private string  GetAccessToken(out  string provider, out string  secret)
    {
        var serviceUser = this.User as  ClaimsPrincipal;
        var ident = serviceUser.FindFirst("http://schemas.microsoft.com/identity/claims/identityprovider").Value;
        string token = "";
        secret = "";
        provider = ident;
        switch (ident)
        {
            case "facebook":
                token = Request.Headers.GetValues("X-MS-TOKEN-FACEBOOK-ACCESS-TOKEN").FirstOrDefault();
                break;
            case "google":
                token = Request.Headers.GetValues("X-MS-TOKEN-GOOGLE-ACCESS-TOKEN").FirstOrDefault();
                break;
            case "microsoftaccount":
                token = Request.Headers.GetValues("X-MS-TOKEN-MICROSOFTACCOUNT-ACCESS-TOKEN").FirstOrDefault();
                break;
            case "twitter":
                token = Request.Headers.GetValues("X-MS-TOKEN-TWITTER-ACCESS-TOKEN").FirstOrDefault();
                secret = Request.Headers.GetValues("X-MS-TOKEN-TWITTER-ACCESS-TOKEN-SECRET").FirstOrDefault();
                break;
        }
        return token;
    }
 
    /// <summary>
    /// Encodes to HMAC-SHA1 used by Twitter OAuth 1.1 Authentication
    /// </summary>
    /// <param name="input">The input string</param>
    /// <param name="key">The input key</param>
    /// <returns>The Base64 HMAC-SHA1 encoded string</returns>
    public static  string Encode(string input, byte[] key)
    {
        HMACSHA1 myhmacsha1 = new  HMACSHA1(key);
        byte[] byteArray = Encoding.ASCII.GetBytes(input);
        MemoryStream stream = new  MemoryStream(byteArray);
        return Convert.ToBase64String(myhmacsha1.ComputeHash(stream));
    }
    /// <summary>
    /// Returns the Unix Timestamp of the given DateTime
    /// </summary>
    /// <param name="dateTime">The DateTime to convert</param>
    /// <returns>The Unix Timestamp</returns>
    public static  long DateTimeToUnixTimestamp(DateTime dateTime)
    {
        return (long) (TimeZoneInfo.ConvertTimeToUtc(dateTime) -
                       new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc)).TotalSeconds;
    }
    /// <summary>
    /// Generates a random number from 123400 to int.MaxValue
    /// </summary>
    /// <returns>A random number as string</returns>
    public static  string GenerateNonce()
    {
        return new  Random()
            .Next(123400, int.MaxValue)
            .ToString("X");
    }
 
}

Don’t forget to change the 2 const fields (line 5, 6) to your Twitter Consumer Key and Secret! (These are the two values you used when you set up your Mobile App with Twitter)***
**
**

The above code reads the user’s access token which comes with every request he makes, invoking the correct endpoint for each provider using GET method and the right parameters, reads the user’s name and image URL and returns them to the user!

In this example,  we are just using these two values, but it’s more or less the same process for pretty much everything like the user’s friends or posts.

You just have to check the HTTP-based API for every provider (e.g. Facebook’s Graph API) to read about how and which endpoint to invoke and how the data is returned.

Application

Okay, we are ready to create our super simple UWP application to connect to our backend, get authenticated and use our custom Web API Controller!

Remember*. The process for the other platforms is exactly the same! Just see the proper documentation on how to do the 3 things I mentioned above and you are done! No changes to the backend.*

 

  1. Create a new UWP Project and name it whatever you like.
  2. Create a new folder and name it Models
  3. Create a new class and name it UserInfo.cs.
  4. Copy the following code inside it. It's exactly the same model class as before.
public class  UserInfo
{
    public string  Name { get; set; }
    public string  ImageUri { get; set; }
 
}
  1. Right click on your UWP project and click **Manage NuGet Packages.**Select the Browse tab if it's not already selected and search for Microsoft.Azure.Mobile.Client and install it.
  2. Open the MainPage.xaml
  3. Delete the root Grid
  4. Add the following code inside your Page element
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
 
        <StackPanel Name="loginSp" RelativePanel.AlignHorizontalCenterWithPanel="True">
            <TextBlock Margin="10" Style="{StaticResource HeaderTextBlockStyle}" Text="Login using.."></TextBlock>
            <Button Margin="5" HorizontalAlignment="Center" Content="Facebook" Click="Login_Click"></Button>
            <Button Margin="5" HorizontalAlignment="Center" Content="Google" Click="Login_Click"></Button>
            <Button Margin="5" HorizontalAlignment="Center" Content="Microsoft Account" Click="Login_Click"></Button>
            <Button Margin="5" HorizontalAlignment="Center" Content="Twitter" Click="Login_Click"></Button>
 
        </StackPanel>
        <StackPanel RelativePanel.AlignHorizontalCenterWithPanel="True" RelativePanel.Below="loginSp" Name="userSp">
            <Image Width="100" Height="100" Name="userImage"></Image>
            <TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Name="userName"></TextBlock>
        </StackPanel>
    </RelativePanel>

That's our View.

 

5. Now, open MainPage.xaml.cs
6. Add the following code

public MobileServiceClient client;
public MobileServiceUser user;
public MainPage()
{
    this.InitializeComponent();
}
 
protected override  void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
 
    client = new  MobileServiceClient("https://uwpstories.azurewebsites.net");
     
}
 
private async void Login_Click(object sender, RoutedEventArgs e)
{
    Button button = sender as  Button;
 
    try
    {
        switch (button.Content as string)
        {
            case "Facebook":
                user = await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook);
                break;
            case "Google":
                user = await client.LoginAsync(MobileServiceAuthenticationProvider.Google);
                break;
            case "Microsoft Account":
                user = await client.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
                break;
            case "Twitter":
                user = await client.LoginAsync(MobileServiceAuthenticationProvider.Twitter);
                break;
        }
    }
    catch (Exception)
    {
 
    }
     
    if (user != null)
    {
        var userInfo = await client.InvokeApiAsync<UserInfo>("UserInfo",HttpMethod.Get,null);
 
        userName.Text = userInfo.Name;
 
        var httpclient = new  HttpClient();
        var bytes = await httpclient.GetByteArrayAsync(userInfo.ImageUri);
        var bi = new  BitmapImage();
        await
            bi.SetSourceAsync(
                new MemoryStream(bytes).AsRandomAccessStream());
 
        userImage.Source = bi;
    }
}

Don't forget to change the Mobile App URL *of your ****MobileServiceClient (Line 12) (https is required!)


The above code uses the **MobileServiceClient **class to connect to our backend.

When you press any of the four buttons which are placed in our View (Facebook, Google, Microsoft Account, Twitter), **client.LoginAsync is used and **a new window opens and the selected provider will ask you for your credentials.

https://uwpstories.files.wordpress.com/2016/05/1.png?w=544

https://uwpstories.files.wordpress.com/2016/05/2.png?w=544

When you successfully log in, client.InvokeApiAsync<UserInfo> is used, our Controller is invoked and when the user's name and image URI are available, they are returned back inside a UserInfo object.

Finally, we just present them on our view using a TextBlock and an Image!

https://uwpstories.files.wordpress.com/2016/05/3.png?w=544

That's it! You can find the complete project here. (GitHub)

Thanks for reading and happy coding!

George

 

Did you find it useful? Write below!