Azure Mobile Services (3.) – Autentizační služby a bezpečnost (aktualizováno)

V prvním dílu totoho seriálu jsme se seznámili s účelem Azure Mobile Services (vyzkoušejte zdarma!) a některými základními fakty. Ve druhém díle jsme poznali datové služby, které nám umožňují ukládat a spravovat data na serveru. Dnes se podíváme na to, jak tato data zabezpečit.

Úrovně zabezpečení dat

Veškerý přístup k datům se děje prostřednictvím HTTP(S) rozhraní typu REST. Každá operace na tomto rozhraním (Read, Insert, Update, Delete) má nastavení úrovně zabezpečení odděleně:

image

K dispozici jsou 4 úrovně zabezpečení:

  1. Everyone – k datům může přistupovat kdokoliv, HTTP požadavky na data nemusí být nijak autentizovány a každý kdo zná příslušné URL může s daty pracovat
  2. Anybody with the application key – HTTP požadavek musí obsahovat v hlavičce aplikační klíč. Tento klíč můžete zjistit na portálu pro správu mobilní služby. Jedná se o výchozí režim zabezpečení, který jsme použili též v příkladu pro práci s daty. Je ovšem třeba si uvědomit, že klíč je v tomto případě distribuován s aplikací. Nejde tedy o nikterak silnou úroveň zabezpečení, neboť jeho získání analýzou balíčku aplikace je sice obtížné, ale určitě ne nemožné.
  3. Only Authenticated Users – pravděpodobně nejuniverzálnější zabezpečený režim, kdy HTTP požadavek musí obsahovat autentizační token (OAuth token) vydaný některým z poskytovatelů identit. Tomuto režimu se budeme věnovat ve zbytku článku.
  4. Only Scripts and Admins – v tomto režimu má přístup k operaci pouze vlastník tzv. master key, který zjistíte opět v portálu pro správu služby. Tento klíč by měl být používán pouze pro automatizovanou správu služby a neměl by být nikdy použit ve vašich aplikacích.

Autentizace uživatelů

Pokud chcete zpřístupňovat data aplikace selektivně v závislosti na uživateli zařízení, musíte uživatele autentizovat. Buď můžete použít nějaké svoje vlastní autenizační údaje – typicky jméno a heslo, které uživatel vzápětí zapomene a založí si jiné. Anebo můžete využít toho, že uživatel již svoji identitu má (Facebook profil, Twitter, LiveID/Microsoft Account, Google ID apod.). Druhá varianta vypadá lákavě, ale je poměrně složitá na ruční implementaci. Mobile Services ale vše zařídí za vás – podporují přitom všechny uživatele identity uvedené v závorce výše.

Pokud chcete tyto poskytovatele identit použít ve vaší aplikaci, je nutno přejít na stránky poskytovatele identity a zaregistrovat si u nich vaši mobilní aplikaci. Postup pro jednotlivé poskytovatele identity je popsán zde:

Princip je ovšem vždy stejný – od poskytovatele identity získáte identifikátor své aplikace a klíč, kterým bude zašifrován vydávaný aplikační token. Tyto údaje je poté nutno přepsat do konfigurace na portálu:

image

Při autentizaci vaší aplikace je pak uživatel přesměrován na stránku příslušného poskytovatele identity, přičemž v hlavičkách požadavku je předán identifikátor vaší aplikace. Po úspěšné autentizaci vrátí poskytovatel identity v HTTP odpovědi autentizační token (OAuth token) zašifrovaný klíčem vaší aplikace. Tento je poté aplikací předán službě Mobile Service, která token rozšifruje a tím ověří jeho správnost a uživatele autentizuje.

Zapojení do vaší aplikace

Zapojení autentizace do vaší aplikace je velmi jednoduché. Základní třída MobileService nabízí následujícím metody:

  • metodu LoginAsync – asynchronní vyvolání přihlašovacího dialogu. Po úspěšném přihlášení jsou všechny datové operace prováděné prostřednictvím této třídy autentizované. Je tedy vhodné mít třídu definovánu jako statickou
  • vlastnost LoginInProgress – vrací hodnotu typu bool indikující, zda právě probíhá autentizace
  • metodu Logout – “zapomenutí” autentizačních údajů službou MobileService
  • vlastnost CurrentUser – nabízí programový přístup ke jménu uživatele a použitému autentizačnímu tokenu.

Nejčastěji budete používat metodu LoginAsync, její volání je opravdu triviální.

C# pro Windows Phone 8 a Windows Store:

await service.LoginAsync(MobileServiceAuthenticationProvider.Facebook);

JavaScript pro Windows Store:

service.login("facebook").done(function (results) {…libovolny…kod…});

JavaScript pro HTML v prohlížeči:

service.login("facebook").then(function (results) {…libovolny…kod…});

Objective C pro iOS aplikace:

[self.service.client
loginViewControllerWithProvider:@"facebook"
completion:^(MSUser *user, NSError *error)
{…libovolny…kod…}]

Java pro Android aplikace:

service.login(
  MobileServiceAuthenticationProvider.Facebook,
  new UserAuthenticationCallback() { … });

Výsledek v aplikaci je samozřejmě závislý na použité platformě a poskytovateli autentizace. Na příkladech níže vidíte použití Google ID ve Windows Store aplikaci a Microsoft Account ve Windows Phone aplikaci:

image    image

Autorizace uživatelů

Služba sama o sobě žádnou autorizaci uživatelů nenabízí. Umožňuje pouze autentizaci uživatele na úrovni HTTP volání a poté předá autentizační údaje serverovému skriptu obsluhujícímu příslušný požadavek. Přidat autorizaci – tedy kontrolu toho, co přesně který uživatel smí a nesmí, je potom věcí vašeho kódu. Objekt typu User nabízí dva základní údaje pro vaši aplikaci:

  • level – úroveň autentizace uživatele, hodnoty jsou anonymous (žádná autentizace), authenticated (předložen platný klíč aplikace nebo autentizační token služby) a admin (předložen platný master key aplikace)
  • userId – identifikátor uživatele obsahující zároveň typ poskytovatele identity

Pokud bychom například chtěli, aby naši aplikaci s knihami používalo více uživatelů, je to poměrně snadné. Stačí nastavit serverový Insert skript pro naši aplikaci tak, aby ukládal též identitu uživatele (příslušný sloupeček v tabulce se vytvoří automaticky, pokud máme povolené dynamické schéma):

function insert(item, user, request) {
item.userId = user.userId;
request.execute();
}

Pak je samozřejmě nutné omezit rozsah vracených záznamů v operaci Read:

function read(query, user, request) {
query.where({ userId: user.userId });
request.execute();
}

V operaci Update pak nepovolíme manipulaci s cizími záznamy (zde je to trochu složitější, neboť musíme vyhledat v databázi aktuálního vlastníka záznamu a porovnat ho s přistupujícím uživatelem):

function update(item, user, request) {
var table = tables.getTable('kniha');
table.where({ id: item.id }).read({
success: function(results) {
if(results[0].userId !== user.userId)
request.respond(statusCodes.FORBIDDEN, 'You may only update your records.');
else
request.execute();
}
});
}

Analogicky zabezpečíme též autorizaci pro operaci Delete:

function del(id, user, request) {
  var table = tables.getTable('kniha');
table.where({ id: id }).read({
success: function(results) {
if(results[0].userId !== user.userId)
request.respond(statusCodes.FORBIDDEN, 'You may only delete your records.');
else
request.execute();
}
});
}

A máme pro dnešek hotovo.

V posledním 4.dílu se budeme věnovat notifikačním službám.

Michael