Autentykacja za pomocą Azure Mobile Services

Tworząc trzecią część artykułu o tworzeniu logiki współdzielonej stwierdziłem że materiału będzie trochę za dużo jak na jeden post, dlatego postanowiłem część zapisać w tym poście. O czym będzie dzisiaj? Przede wszystkim o tym jak rozszerzyć możliwość autentykacji jaką daje nam WAMS (Windows Azure Mobile Services). Czasami bowiem poza tym że chcemy wiedzieć czy użytkownik posiada konto w danym serwisie, chcemy również poznać jego imię, czy zapisać sobie jego avatar.

Pierwszym co musimy zrobić to wygenerować sobie odpowiednie klucze do serwisu z którego będziecmy chcieli skorzystać. Microsoft zapewnia nam instrukcje jak wygenerować te klucze dla:

* – mała wskazówka: po wygenerowaniu kluczy wchodzimy w zakładkę “Status & Review” i zaznaczamy checkbox “Do you want to make this app and all its live features available to the general public?” na Tak. Pozwoli to na logowanie innych użytkowników, nie tylko użytkownika, który stworzył aplikacje ;)

** – z powodu że api google się zmieniło, tutorial jest nieaktualny, możliwe że go zaktualizują w przyszłości.

Mając już wygenerowane klucze możemy dodać autentykacje do naszej aplikacji. Jeśli wszystko pójdzie dobrze i użytkownik zaloguje się na poprawne konto dostaniemy jego UserId. Możemy więc dodać go sobie do naszej bazy danych przy okazji wyciągając inne dane. Przechodzimy zatem do naszego panelu Azure Mobile Services, tworzymy nową tabelkę Users i dajemy pozwolenie na dodawanie tylko zalogowanym użytkownikom.

blog-wams-users-table

Następnie Dodajemy sobie kolumny Name oraz AvatarUrl (obie są stringami). Na koniec przechodzimy na zakładkę Script i podmieniamy skrypt Insert na przedstawiony poniżej.

function insert(item, user, request) {
    var req = require('request');

    var getFacebookPic = function()
    {
        var facebookId = user.userId.replace("Facebook:", "");
        var facebookReqOptions = {
                    uri: 'http://graph.facebook.com/' + facebookId + '?fields=picture.type(small)',
                    headers: { Accept: "application/json" }
                };
        req(facebookReqOptions, function(err, resp, body){
            var jsonBody = JSON.parse(body);
            item.AvatarUrl = jsonBody.picture.data.url;
        });
    }

    var getMicrosoftPic = function(userId){
        var link = 'https://cid-[userId].users.storage.live.com/users/0x[userId]/myprofile/expressionprofile/profilephoto:Win8Static,UserTileMedium,UserTileStatic';
        item.AvatarUrl = link.replace(/[userId]/g, userId);       
    }

    item.Nick = "<unknown>"; // default
    user.getIdentities({
        success: function (identities) {
            var msToken;
            var url = null;
            var oauth = null;
            if (identities.google) {
                var googleAccessToken = identities.google.accessToken;
                url = 'https://www.googleapis.com/oauth2/v3/userinfo?access_token=' + googleAccessToken;
            } else if (identities.facebook) {
                var fbAccessToken = identities.facebook.accessToken;
                url = 'https://graph.facebook.com/me?access_token=' + fbAccessToken;
            } else if (identities.microsoft) {
                var liveAccessToken = identities.microsoft.accessToken;
                url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + liveAccessToken;
                msToken = liveAccessToken;
            } else if (identities.twitter) {
                var userId = user.userId;
                var twitterId = userId.substring(userId.indexOf(':') + 1);
                url = 'https://api.twitter.com/1.1/users/show.json?user_id=' + twitterId;
                var consumerKey = process.env.MS_TwitterConsumerKey;
                var consumerSecret = process.env.MS_TwitterConsumerSecret;
                oauth = {
                    consumer_key: consumerKey,
                    consumer_secret: consumerSecret,
                    token: identities.twitter.accessToken,
                    token_secret: identities.twitter.accessTokenSecret
                };
            }

            if (url) {
                var requestCallback = function (err, resp, body) {
                    if (err || resp.statusCode !== 200) {
                        console.error('Error sending data to the provider: ', err);
                        request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);
                    } else {
                        try {
                            var userData = JSON.parse(body);
                            item.Nick = userData.name;
                            if (identities.twitter) {
                                item.AvatarUrl = userData.profile_image_url;
                            } else if (identities.facebook){
                               getFacebookPic();
                            } else if (identities.microsoft){
                               getMicrosoftPic(userData.id);
                            }
                            request.execute();
                        } catch (ex) {
                            console.error('Error parsing response from the provider API: ', ex);
                            request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);
                        }
                    }
                }

                var reqOptions = {
                    uri: url,
                    headers: { Accept: "application/json" }
                };
                if (oauth) {
                    reqOptions.oauth = oauth;
                }
                req(reqOptions, requestCallback);
            } else {
                // Insert with default user name
                request.execute();
            }
        }
    });

}

Skrypt ten zapisuje nazwę użytkownika w polu Name, oraz dla autentykacji poprzez Facebooka, Twittera oraz Microsoft Account zapisuje link do avatara. Niestety dla Google Account nie mogłem wygenerować kluczy więc nie miałem jak sprawdzić skąd wyciągnąć avatar.