Archive for január, 2009

Tippek és trükkök

Összejött pár apró tipp és trükk, melyek önmagukban nem töltenek ki egy blogbejegyzést, de adott esetben fontos lehet tudni ezekről a megoldásokról.

A következőkről lesz szó:

  • Flash beágyazása az oldalba
  • Szabványbarát doctype használata (IE6 quirks mode mellőzése)
  • Reklámok kirakása (külső scriptek kimenetének kezelése)
  • Térképek (Google/Yahoo/Live Maps) megjelenítése
  • Profil nézetből Canvas nézetre navigálás

Flash beágyazása

Ha Flash-t szeretnénk betenni az oldalunkba, a legjobb megoldás a gadgets.flash.embedFlash használata lehet. A függvénynek meg kell adni annak a HTML elemnek az id-ját, ahova a Flash-t szánjuk, illetve kell egy <Require feature=”flash” /> is a fejlécbe.

Ha JavaScript-Flash kommunikációt szeretnénk, vagy egyéb Flash lehetőséget, akkor figyeljünk az átadott paraméterekre. Fontos még az is, hogy az embedFlash függvényt init után hívjuk meg.

A beágyazás tehát valahogy így fog működni:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="iWiW Flash" author="DevBlogger" author_email="dev@iwiw.hu" height="600">
<Require feature="opensocial-0.8" />
<Require feature="flash" />
</ModulePrefs>
<Content type="html"><![CDATA[
<script type="text/javascript">
function init() {
  gadgets.flash.embedFlash('http://videa.hu/flvplayer.swf?v=F0cKU2XjFGTy6VMX', 'videa', 8,
    { 'width': 750, 'height': 600, 'allowFullScreen': true, 'allowscriptaccess': 'always' });
}
gadgets.util.registerOnLoadHandler(init);
</script>
<div id="videa"></div>
]]></Content>
</Module>

DocType módosítás

A hivatalos OpenSocial szabvány szerint az iframe-en belüli HTML kód DOCTYPE nélkül töltődik be, vagyis úgynevezett quirks módban jelenik meg. Ezzel egy nagy gond van: IE6 alatt a doboz modell jelentősen eltér a szabványostól, ami megnehezíti egy minden böngészőben jól működő layout kivitelezését.

A megoldás egy a szabványon túllépő kiegészítés (ha minden igaz, az OpenSocial 0.9 valahogy megoldja ezt a kérdést, de szerettünk volna addig is valamilyen megoldást kínálni), miszerint hogyha a Content elemen belül teljes HTML írunk, akkor nem csak a BODY elemen belüli részt befolyásolhatjuk, hanem a teljes oldal kódját.

Vagyis ha át szeretnénk billenteni szabványos módba az IE6 alatt is a dobozmodellt, akkor a következőképpen kell eljárnunk:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="DocType teszt" author="DevBlogger" author_email="dev@iwiw.hu">
  <Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html" view="canvas, profile"><![CDATA[
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
Hello IE6 standards mode!
</body>
</html>
]]></Content>
</Module>

Térképek

Térképek beszúrását igazán csak a Google nehezítette meg, mivel külön fejlesztői kulcsot kell kérnünk domainenként, de jöjjön egy példa a három legnagyobb térkép szolgáltatást illetően. További infókat az egyes szolgáltatások doksijaiban lehet találni.

A Google Maps-hez két kulcsra lesz szükségünk. Mindenekelőtt ezeket szerezzük be a Google Maps API oldalán. Ehhez az alkalmazásunk azonosítójára lesz szükség, ami egy többjegyű szám, pl: 123456789 (a sandboxban megnézhetjük) — a http://123456789.app.sandbox.iwiw.hu és a http://123456789.app.iwiw.hu domainekre kell kérnünk egy-egy kulcsot. Ha ezzel megvagyunk, a következő kód segítségünkre lehet (a kódba írjuk a kulcsokat bele a megfelelő helyre!):

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Google Maps" author="DevBlogger" author_email="dev@iwiw.hu" height="400">
  <Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html"><![CDATA[
<script type="text/javascript">
// kulcsok
var GMapKeys = {
  "sandbox.iwiw.hu": "...fejlesztői.kulcs...",
  "iwiw.hu": "...fejlesztői.kulcs..."
}
 
// az aktuális kulcs kiválasztása
var GMapKey = GMapKeys[opensocial.getEnvironment().getDomain()];
 
// egy új <script> elem létrehozása és felparaméterezése
document.write('<scri'+'pt type="text/javascript" src="http://maps.google.com/maps?file=api&v=2&key=' + GMapKey + '"></sc'+'ript>');
</script>
 
<script type="text/javascript">
 
function init() {
  if (typeof GBrowserIsCompatible == 'function' && GBrowserIsCompatible()) {
    var map = new GMap2(document.getElementById("map"));
    map.setCenter(new GLatLng(37.4419, -122.1419), 13);
  }
}
 
// beindítjuk az alkalmazást
gadgets.util.registerOnLoadHandler(init);
 
</script>
<div id="map" style="height: 400px;"></div>
]]></Content>
</Module>

Yahoo! Maps esetén szintén kell kulcsot kérnünk, ám ez nem domainhez, hanem alkalmazáshoz kötött, így nem kell két külön kulcs a Homokozóhoz és az éles rendszerbe. Fejlesztéshez, kipróbáláshoz használhatjuk a YD-eQRpTl0_JX2E95l_xAFs5UwZUlNQhhn7lj1H kulcsot, de élesbe ezzel nem mehet az alkalmazásunk (mert letiltja a Yahoo! a térképet). A kulcsot a SCRIPT elem végére kell beírnunk:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Yahoo! Maps" author="DevBlogger" author_email="dev@iwiw.hu" height="400">
  <Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html"><![CDATA[
<script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap?v=3.8&appid=...fejlesztői.kulcs..."></script>
<script type="text/javascript">
 
function init() {
  var map = new YMap(document.getElementById('map'));
  map.addTypeControl();
  map.setMapType(YAHOO_MAP_REG);
  map.drawZoomAndCenter("Budapest", 3);
}
 
// beindítjuk az alkalmazást
gadgets.util.registerOnLoadHandler(init);
 
</script>
<div id="map" style="height: 400px;"></div>
]]></Content>
</Module>

Végül nézzük a Microsoft Virtual Earth / Live Maps megoldást, aholis nincs szükségünk kulcsra:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Live Maps" author="DevBlogger" author_email="dev@iwiw.hu" height="400">
  <Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html"><![CDATA[
<script charset="UTF-8" type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&mkt=en-us"></script>
<script type="text/javascript">
 
function init() {
  var map = new VEMap('map');
  map.LoadMap();
}
 
// beindítjuk az alkalmazást
gadgets.util.registerOnLoadHandler(init);
 
</script>
<div id="map" style="height: 400px;"></div>
]]></Content>
</Module>

Reklámok beszúrása

Egy reklám blokkot (bannert) nem nehéz beszúrni, egy SCRIPT/NOSCRIPT párost lehet kapni az Origotól, amit a megfelelő HTML pozícióba betehetünk. Problémát okozhat viszont, hogy ha olyan oldalrészbe szeretnénk a reklámot megjeleníteni, ami induláskor még nem létezik, mert az alkalmazás futása során generáljuk, hozzuk létre. Ekkor az egyik lehetséges trükk az, hogy a generált HTML részbe bemásoljuk egy olyan helyről, amit induláskor hoztunk létre. Az alábbi kód ezt csinálja (egy egyszerű SCRIPT elemmel, nem konkrét bannerkóddal):

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Hello iWiW" author="DevBlogger" author_email="dev@iwiw.hu">
  <Require feature="opensocial-0.8" />
  <Require feature="views" />
</ModulePrefs>
<Content type="html"><![CDATA[
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script type="text/javascript">
 
var App = {
 
  // ezt a függvényt hívjuk meg az alkalmazás betöltődésekor
  init: function() {
    var ad = document.getElementById("ad");
    var contentAd = document.getElementById("content-ad");
    contentAd.innerHTML = ad.innerHTML;
  }
 
};
 
// az alkalmazás betöltődésekor meghívandó függvényt adjuk át, hasonlít a
// weblapok onload függvényéhez
gadgets.util.registerOnLoadHandler(App.init);
 
</script>
<div id="ad-container" style="display: none;">
    <div id="ad">
	<!-- a banner kódja -->
	<script>document.write("ad");</script>
	<!-- a banner kódja -->
    </div>
</div>
 
<div id="content">
    <p>Ez lenne a generált tartalom.</p>
    <div id="content-ad">ide jön a reklám</div>
    <p>Itt a vége, fuss el véle.</p>
</div>
 
]]></Content>
</Module>

Canvas nézetre navigálás

Ha át szeretnénk navigálni a Canvas nézetre, akkor ehhez be kell töltenünk a views kiegészítést a fejlécben (<Require feature=”views” />), illetve a következő függvényt kell meghívni:

gadgets.views.requestNavigateTo(gadgets.views.getSupportedViews()["canvas"], {});

A kapcsos zárójelek között átadhatunk paramétereket is, melyeket a következő módon olvashatunk ki a Canvas nézetben:

var params = gadgets.views.getParams()

A profil oldali megjelenést célszerű úgy kialakítani, hogy a legfontosabb információkat gyűjtse össze, és kvázi a canvas nézet reklámjaként, funkcionális bannerként működjön - ehhez nyújthat segítséget az átnavigálás lehetősége.

Az összes ismerős lekérdezése

UPDATE: Fontos változás az ismerősök lekérdezésénél.

Az iWiW alkalmazások lekérdezhetik az alkalmazás tulajdonosának, illetve az alkalmazás nézőjének ismerőseit (erről már volt korábban szó: Mit tehet egy alkalmazás?), ám ezt illetően vannak kötöttségek, például egy lekérdezés után alapértelmezetten csak 20 ismerőst kapunk vissza, s bár ezt meg lehet növelni, de a limit 50 ismerős - vagyis lapozni kell. Az alábbiakban egy megoldást teszek közzé, mely felhasználóbarát módon teszi lehetővé az összes ismerős elérését.

A megoldás kulcsához nézzük át, hogy a newFetchPeopleRequest kérés milyen módon paraméterezhető:

  • FILTER: Szűrhetjük az ismerősök listáját, például szűkíthetjük az ismerősök listáját azokra, akiknek a jelen alkalmazás már fel van telepítve. Jelenleg csak ezt a szűrési módot támogatja az iWiW.
  • FILTER_OPTIONS: Egyes filterekhez lehet(ne) megadni további paramétereket segítségével.
  • FIRST: Ezzel határozhatjuk meg, hogy hányadik ismerőstől kérjük a listát.
  • MAX: Ezzel pedig azt, hogy maximum mennyi elemet kérünk (50-nél többet nem kérhetünk az iWiW-en).
  • PROFILE_DETAILS: Milyen profil tulajdonságokat szeretnénk lekérni - jelenleg felesleges használni, de a jövőben amikor további profil információkhoz is hozzá lehet majd férni, akkor igen hasznos lesz.
  • SORT_ORDER: Milyen sorrendben szeretnénk az ismerőseinket visszakapni. Jelenleg csak a névsort támogatja az iWiW, vagy ha nem adunk meg sorrendet, akkor valamilyen “véletlen” (belső azonosítóra épülő), de nem változó listát kapunk.

A trükkre az eddig írtakból rá lehet jönni: a FIRST és MAX paraméterek állításával fogjuk lekérdezni az összes ismerőst. Van még két trükk azonban: az egyik, hogy a kéréseket összecsomagolva csökkenthetjük a szerver felé indított kérések számát, illetve hogy először csak egy csomagot (50 ismerőst) kérünk le, s a válaszban benne levő teljes ismerősök száma tulajdonságot használjuk fel ahhoz hogy meghatározzuk, mennyi további csomagot fogunk lekérni (300 ismerősnél első kérésben 50-et, második kérésben 5 csomagban 5×50-et, avagy 250-et fogunk lekérni).

Ennek a felállásnak az is előnye, hogy az első válasz után már meg tudjuk jeleníteni a felhasználók felé az első adagot, s míg jön a többi, ő már elkezdhet tevékenykedni.

Ezekkel a funkciókkal, lehetőségekkel összeállítottam egy kész függvényt, mely ezeket a feladatokat megcsinálja mind helyettünk:

var IWIW = {};
IWIW.loadFriends = function(options) {
  // az objektumot később is fogjuk még használni, amikor a "this" más értékkel fog
  // bírni, ezért letároljuk az "obj" változóba
  var obj = this;
  // az ismerősök száma
  obj.totalSize = 0;
  // a params paraméterrel átadhatunk paramétereket a newFetchPeopleRequest hívásnak
  obj.params = options.params || {};
  // az ismerősök lekérdezését a legelején kezdjük
  obj.params[opensocial.DataRequest.PeopleRequestFields.FIRST] = 0;
  // és 50-et kérünk le (iWiW-en az egy csomagban lekérhető maximum)
  obj.params[opensocial.DataRequest.PeopleRequestFields.MAX] = 50;
  // a userId paraméter segítségével megmondhatjuk, hogy az OWNER vagy VIEWER
  // ismerőseire vagyunk-e kíváncsiak
  var userId = options.userId;
  // az onFirstFriends egy függvény, mely akkor hívódik meg, amikor az első ismerős
  // csomag megérkezik
  var onFirstFriends = options.onFirstFriends || function(){};
  // az onAllFriends egy függvény, mely akkor hívódik meg, amikor az összes ismerős
  // csomag megérkezett
  var onAllFriends = options.onAllFriends || function(){};
  // egy adatlekérés objektumot hozunk létre
  var request = opensocial.newDataRequest();
  // egy csoportot definiálunk: az alkalmazás nézőjének vagy tulajdonosának (userId) az ismerőseit
  obj.usergroup = opensocial.newIdSpec({"userId": userId, "groupId": "FRIENDS"});
  // a kéréshez hozzáadjuk az előbb definiált csoport lekérdezését, a választ
  // "friends0" néven fogjuk visszakapni
  request.add(request.newFetchPeopleRequest(obj.usergroup, obj.params), "friends0");
  // a függvény, mely az első adatlekérés után fut le
  var firstReady = function(response) {
    // itt a this értéke különbözik az eredeti objektumunktól,
    // ezért is használjuk az obj változót.
    // az obj.friends egy tömb lesz, melybe a válasz csomagokat pakoljuk
    obj.friends = [response.get("friends0").getData()];
    // a válaszban benne van az az információ, hogy összesen mennyi ismerős van, letároljuk
    obj.totalSize = obj.friends[0].getTotalSize();
    // a size értéke a jelen csomagban érkezett ismerősök száma (jellemzően 50, hacsak nincs
    // 50-nél kevesebb ismerőse a felhasználónak)
    var size = obj.friends[0].size();
    // a pozíció, ahol az ismerősök listájával járunk (0, 50, 100, stb. lesz)
    var pos = 0;
    // ha nem jött meg az összes ismerős, akkor...
    if (size < obj.totalSize) {
      // meghívjuk az első ismerősök megérkezésekor lefutó függvényt
      onFirstFriends(obj);
      // egy adatlekérés objektumot hozunk létre
      var request = opensocial.newDataRequest();
      // amíg nem érjük el az ismerősök számát...
      while (pos + size < obj.totalSize) {
        // következő csomag ott kezdődik, ahol az előző abbamaradt
        pos += size;
        // beállítjuk, hogy honnantól kezdje letölteni az ismerősöket, mi legyen az első elem
        obj.params[opensocial.DataRequest.PeopleRequestFields.FIRST] = pos;
        // a kéréshez hozzáadjuk a korábban már definiált csoport lekérdezését, a választ
        // "friendsXXX" néven fogjuk visszakapni, ahol az XXX a pozíció
        request.add(request.newFetchPeopleRequest(obj.usergroup, obj.params), "friends"+pos);
      }
      // elküldjük a több csomagot is tartalmazó kérést (második kérés)
      request.send(allReady);
    } else {
      // ha az első kérés során megjött az összes ismerős, akkor csak az onAllFriends kerül
      // meghívásra, mert már most tartalmazza az összes ismerőst.
      onAllFriends(obj);
    }
  }
  // a függvény, melyet a második lekérdezés válaszának megérkezésekor hívunk meg
  var allReady = function(response) {
    // csomag változó
    var package;
    // az első csomag végéről indulunk
    var pos = obj.friends[0].size();
    // ha nem értünk a végére, akkor...
    while (pos < obj.totalSize) {
      // a csomagot kivesszük a válaszból
      var package = response.get("friends"+pos).getData();
      // és betesszük az obj.friends tömbbe
      obj.friends.push(package);
      // növelkjük a pozíciót
      pos += package.size();
    }
    // meghívjuk az onAllFriends függvényt az összes ismerőssel
    onAllFriends(obj);
  }
  // elküldjük a lekérdezést, a függvény paramétere egy másik függvény,
  // ami akkor kerül meghívásra, amikor a szervertől megérkezik a válasz
  request.send(firstReady);
  // visszatérünk az IWIW.loadFriends objektumunkkal, ez egyelőre elég üres
  // de már beindítottuk az első lekérdezést, melyre hamarosan megjön a válasz
  return this;
};

A függvényt igyekeztem elég jól dokumentálni sok-sok megjegyzéssel, a kódban benne van tehát a magyarázat - emiatt nem írnám le még egyszer hogy pontosan hogyan működik. Összefoglalva: egy vagy két kérést indítunk a szerver felé, második kérést csak akkor, ha az első 50 elembe nem fért bele az összes ismerős. A második kérésben annyi csomagban kérjük le az ismerősöket, hogy immár a teljes ismerősi kör beleférjen. Paraméterként két függvényt adhatunk át, az első az első kérés után fut le (ha nem jött meg az összes ismerős), míg a második akkor, ha megjött az összes. A megkapott objektum friends tömbjében lesznek benne a szervertől visszakapott válaszok.

A függvény használatára egy példa, így valószínűleg még érthetőbb lesz:

// új kérés
new IWIW.loadFriends({
  // a tulajdonos ismerőseit fogjuk lekérdezni
  userId: "OWNER",
  // ha megjött az első adag ismerős
  onFirstFriends: function(response) {
    // az objektum friends tulajdonsága a fentebb emlegetett tömb
    var friends = response.friends;
    // a friends tömbnek még csak egy eleme van
    friends[0].each(function(person){
      // person.getDisplayName() használható, például
    })
  },
  // ha megjött az összes ismerős
  onAllFriends: function(response) {
    // az objektum friends tulajdonsága a fentebb emlegetett tömb
    var friends = response.friends;
    // végigmegyünk a tömb elemein, a csomagokon
    for(var i=0; i<friends.length; i++) {
      // minden csomagból kiolvassuk a benne levő személyeket
      friends[i].each(function(person){
        // person.getDisplayName() használható, például
      })
    }
  }
});

Megnyílt a jóváhagyói rendszer és a Versenyre nevezés

Élesítettük a jóváhagyói rendszert és a versenyre nevezést, így ha elkészültetek az alkalmazásokkal és sikeresen kiteszteltétek azokat a Homokozón, most már be tudjátok küldeni jóváhagyásra, illetve ezzel együtt a Versenyre is ezeket.

Az alkalmazások jóváhagyásra beküldése a Fejlesztői Portálon az egyes alkalmazások Vezérlőpultján érhető el, a Homokozóban frissítés blokk alatt:

jovahagyas_kerese

A “Jóváhagyás kérése” gomb megnyomása után egy fejlesztői kérdőívet kell kitölteni az alkalmazásról, illetve a kérdőív végén van mód jelezni, ha valaki a Versenyre is szeretné nevezni az alkalmazást. (Ez utóbbi opcionális. A Versenyre nevezés határideje február 15.)

Kérjük, hogy csak az általatok hibamentesnek tekintett és véglegesnek szánt alkalmazásaitokat küldjétek be jóváhagyásra.

A jóváhagyásra beküldés után technikai, üzleti és jogi szempontokból is megvizsgáljuk az alkalmazást, és annak komplexitása függvényében mielőbb reagálunk. Az elbírálás eredménye ideális esetben természetesen az lesz, hogy az alkalmazás kikerülhet majd az éles iWiW-re, de az is elképzelhető, hogy technológiai javítási javaslatok vagy üzleti egyeztetés végett felvesszük a kapcsolatot a fejlesztővel.

Elindult a fórum

Több fronton is kommunikálunk veletek: itt a blogon, emailben (dev kukac iwiw pont hu), wikiben, twitteren. A nemrégiben elindult fórumunk ezeket a felületeket egészíti ki most, behozva a közösségi szálat. Mindegyik kommunikációs csatornának más a célja, elgondolásai - ezeket gyűjtöttem most össze, hogy érthetőbb és hatékonyabb legyen a felállás.

A fórumon, melyet a belépett fejlesztők érnek el, a több fejlesztőt is érintő témákat lehet felvetni, itt adunk teret a fejlesztők közötti együttműködésre is. Cél az is, hogy a publikus kérdésekre, problémákra a lehető legtöbben lássák a válaszokat. A fórumot legkönnyebben a friss hozzászólások listájának figyelésével lehet követni.

E-mailben azoknak tudunk segíteni, akiknek inkább másokra nem tartozó, egyedi problémájuk van, forduljatok hozzánk bizalommal.

Itt a blogon a hosszabb híreket, illetve technikai leírásokat, információkat tesszük közzé. Mivel fontos híreket, és példákkal, magyarázatokkal tűzdelt leírásokat osztunk meg, napi szintű követését kiemelten javaslom. Ebben segít, ha az RSS olvasótokba  (Netvibes, Google Reader, stb.) felveszitek a blog feedjét, illetve ha hozzászóltok egy bejegyzéshez, akkor a hozzászólásnál feliratkoztok a további hozzászólásokra (melyekről így emailben értesítést küld a rendszer).

A twitter felhasználónk a blog hírszolgáltatását egészíti ki. Rövid és az adott pillanatban aktuális híreket teszünk itt közzé (a Fejlesztői Portált érintő fejlesztéseket, frissítéseket, hibajavításokat jelentjük, illetve a blog bejegyzéseit promózzuk). Ezeket a híreket minél hamarabb szeretnénk eljuttatni hozzátok. Twitterhez számos kliensalkalmazás van, de egyszerűen a Twitter feedünkre is fel lehet iratkozni.

Végül, de nem utolsó sorban a Wiki az API technológiai alapismeretek közlésére szolgál. Itt olvashatóak azok az alapok, melyek elengedhetetlenül szükségesek lehetnek az alkalmazásfejlesztéshez, s idővel ide kerülnek majd be kicsit átszerkesztett formában a blogon megjelent technikai információk is.

Aláírt kommunikáció (signed request)

Az alkalmazások lehetőségeit bemutatva írtam már róla, hogy az alkalmazás kommunikálni tud a külvilággal is, és megemlítettem, hogy lehetőség van ezt a kommunkációt hitelesíteni (ez az aláírt kommunikáció), vagyis bizonyíthatóan igazolni, hogy mely féltől érkezett a kérés. A bejegyzésben arról írok, hogy hogyan működik, mire használható ez a lehetőség.

Aláírt kommunikációra akkor van (leginkább) szükségünk, amikor egy felhasználó nevében saját szolgáltatáshoz fordulunk, és szeretnénk tudni, hogy:

  • valóban az iWiW felől jött-e a HTTP kérés
  • mely felhasználótól jött a kérés
  • mely alkalmazástól jött a kérés

A felhasználói azonosítót ugyan átadhatjuk egy sima GET paraméterben is, továbbá azt is nézhetjük hogy mely IP címek felől érkezett a kérés, ellenben ezek nem sorolhatóak a biztonságos és jó megoldások körébe, mivel nem túl megbízhatóak. Például lehetséges egy adott alkalmazás oldalon, annak nevében kéréseket indítani - ekkor ha az alkalmazás teszi be a felhasználó azonosítóját a paraméterek közé akkor azt könnyen ki lehet cserélni, és más felhasználó nevében is lehet ténykedni (vagy olyan információhoz is hozzá lehet jutni, mely nem tartozik a trükközőre). Az IP címek is változhatnak idővel különösebb értesítés nélkül, az pedig nem jó ha emiatt akármennyi időre is működésképtelen lesz az alkalmazásunk, mert nem fogadja el az új címről a kéréseket.

A biztonságos megoldást az aláírt kérések jelentik, melyek használata nagyon egyszerű: a kérésben elküldött (GET/POST) paramétereket az iWiW OpenSocial motorja kiegészíti további paraméterekkel, többek között egy olyan ellenőrzőösszeggel, mely segítségével ellenőrizhető a kérés forrása. A kiegészítő információk a következők:

  • opensocial_owner_id: az alkalmazás tulajdonosa
  • opensocial_viewer_id: az alkalmazás nézője
  • opensocial_app_id: az alkalmazás azonosítója
  • opensocial_app_url: az alkalmazás XML-jének url-je

Az ellenőrzőösszeget a folyamat az összes paramétert figyelembe véve állítja elő felhasználva egy speciális kulcs értéket is, a végeredmény pedig a az oAuth szabvány aláírás szabályainak megfelelően képződik (az aláírt kéréseknek ennyi, és csak ennyi a köze az oAuthhoz, amiről még lesz szó, és ami amúgy teljesen másra jó). Az ellenőrzőösszeget kell leellenőriznünk a szerver oldalon, mert ez az, amit csak és kizárólag az iWiW fog tudni létrehozni, más “támadó harmadik fél” nem. Ha ez stimmel, akkor biztosak lehetünk benne, hogy az előbb felsorolt paraméterek is hitelesek, megbízhatóak.

Az oAuth szabvány nem definiálja kizárólagosan hogy mely algoritmust kell használni az aláírások készítéséhez, ezt a megvalósításra bízza rá. Az iWiW a következő algoritmusokat támogatja:

  • RSA-SHA1: Egy egy nyílt kulcsú titkosítást használó algoritmus, avagy egy titkos és egy publikus kulcs segítségével történik az aláírás. Ezt az algoritmus az alapértelmezett iWiW kulccsal használható signed request kérések esetén. A titkos kulcs az iWiW-nél van és ezzel készül az ellenőrzőösszeg, míg a publikus kulcs segítségével ellenőrizhető hogy valóban az iWiW-től jött-e a kérés.
  • HMAC-SHA1: Ez egy megosztott rejtett kulcsot használó algoritmus, avagy egy darab kulcs van csak, melyet mind titkosításkor, mind pedig ellenőrzéskor használni kell. Ez az algoritmus a fejlesztő által feltöltött kulcsokkal használható signed request és oAuth kérések esetén.
  • HMAC_SYMMETRIC: ugyanúgy működik, mint a HMAC-SHA1.

Ennyit az elméletről, lássuk hogy hogyan működik mindez a gyakorlatban. Alapértelmezett esetben egy alkalmazásnál az RSA-SHA1 algoritmussal, az iWiW alapértelmezett kulcsaival működik az aláírás. A kérés indítása az alkalmazásunkban:

gadgets.io.makeRequest(
  "http://iwiw.example.com/request.url?param=iWiW",
  function(data){ alert(data.text); },
  {
    METHOD: gadgets.io.MethodType.GET,
    CONTENT_TYPE: gadgets.io.ContentType.TEXT,
    AUTHORIZATION: gadgets.io.AuthorizationType.SIGNED
  }
);

A szerver oldalt PHP-ben valósítottam meg, ehhez egy kész oAuth függvénykönyvtárat használtam, az oAuth.php fájl kellett belőle, ez lett betöltve. Más szerver oldali nyelvet használóknak segítséget nyújthat az oAuth oldalán levő felsorolás, az elv mindenhol ugyanaz, az elterjedtebb programozási nyelvekhez kész kódok érhetőek el.

A PHP kód tehát:

<?
include "oAuth.php";
 
class IWiWSignatureMethod extends OAuthSignatureMethod_RSA_SHA1 {
  protected function fetch_public_cert(&$request) {
    return <<<EOD
-----BEGIN CERTIFICATE-----
MIICRTCCAa4CCQCJTQjR2NRBSTANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJI
VTERMA8GA1UECBMIQnVkYXBlc3QxETAPBgNVBAcTCEJ1ZGFwZXN0MREwDwYDVQQK
EwhJV0lXIEx0ZDENMAsGA1UECxMEaVdpVzEQMA4GA1UEAxMHaXdpdy5odTAeFw0w
ODA5MTgxMDAzMDVaFw0wOTA5MTgxMDAzMDVaMGcxCzAJBgNVBAYTAkhVMREwDwYD
VQQIEwhCdWRhcGVzdDERMA8GA1UEBxMIQnVkYXBlc3QxETAPBgNVBAoTCElXSVcg
THRkMQ0wCwYDVQQLEwRpV2lXMRAwDgYDVQQDEwdpd2l3Lmh1MIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQCgSymvAxyuISIufaSFEAiP1WpivlIlvIa7eIzpjA7H
JqmGtJzosi6IihqFTwthiriZ09d1J4R6jTeqm/CXKGcmUwTqBULxbZC1Yw6dGJMw
FjacooFvu5iHVAM3OFyG0DsMZWwT/XSexybc1+vPHdwkMSgZOg/FgoivahZlV3Mt
8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACfd/y/nAXGXFShDXzFhsDC43izjIg13
STzhIUzsXVFg+zhik7XghjMsohMF99n6GYdRFIoRZpqMxIYQnnw5i10FIu57U39j
kwo4wtCVsSDq/C5un2kcTPJL+kH6P/8OzsaRRuuCn5N7rjGNtB4LvX/JIIxEfS+/
KR+yjqXrml+O
-----END CERTIFICATE-----
EOD;
  }
}
 
$request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST));
$signature_method = new IWiWSignatureMethod();
@$signature_valid = $signature_method->check_signature($request, null, null, $_GET["oauth_signature"]);
if ($signature_valid == true) {
  echo 'Hello '.$_GET['param'].'!';
  // echo $_GET['opensocial_owner_id'];
  // echo $_GET['opensocial_viewer_id'];
} else {
  echo 'Sikertelen hitelesítés.';
}
?>

Először definiálunk egy IWiWSignatureMethod osztályt mely a OAuthSignatureMethod_RSA_SHA1 osztály leszármazottja lesz, s amit az ellenőrzőösszeg kiszámításához fogunk felhasználni. Létrehozunk egy OAuthRequest objektumot, ami mind a GET-es, mind pedig a POST-os paramétereket megkapja, példányosítjuk az előbb létrehozott IWiWSignatureMethod osztályt, és meghívjuk a check_signature metódusát, mely le fogja ellenőrizni, hogy az oauth_signature GET paraméterben megfelelő érték jött-e. Ha igen, akkor visszaküldjük a kapott paramétert egy “Hello” előtaggal, ha nem, akkor megmondjuk hogy rossz volt a kérés.

Ha feltöltöttünk már egy kulcsot, akkor az aláírt kérések nem az iWiW alap kulcsát, hanem az általunk feltöltött kulcsot fogják használni, ezekhez HMAC_SHA1 vagy HMAC_SYMMETRIC kódolást választhatunk. Ezek ahogy az előbb is említettem, nem publikus és titkos kulcspárt használnak, hanem egy megosztott rejtett kulcsot. Ez a kulcs “bármilyen” szövegrészlet lehet, persze érdemes egy megfelelően hosszú, véletlenszerűhöz közelítő, nem kitalálható kulcsot választani. A következő tehát rossz példa:

hmac_sha1_pelda

Az előbb használt PHP-s oAuth kódkönyvtár a HMAC-SHA1 kódolást támogatja, ezért én ezt választottam. A kliens oldali kódunk teljesen változatlan, míg a szerver oldali kód a következőképpen alakul:

<?                                                                                                                                                                                 
include "oAuth.php";                                                                                                                                                               
 
$request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST));                                                                                                   
$consumer = new OAuthConsumer($_GET['oauth_consumer_key'], 'teszt kulacs', NULL);                                                                                                
$signature_method = new OAuthSignatureMethod_HMAC_SHA1();                                                                                                                        
@$signature_valid = $signature_method->check_signature($request, $consumer, null, $_GET["oauth_signature"]);                                                                     
if ($signature_valid == true) {
  echo 'Hello '.$_GET['param'].'!';
  // echo $_GET['opensocial_owner_id'];
  // echo $_GET['opensocial_viewer_id'];
} else {
  echo 'Sikertelen hitelesítés.';
}
?>

Először létrehozunk egy OAuthRequest objektumot, ami mind a GET-es, mind pedig a POST-os paramétereket megkapja, példányosítunk egy OAuthConsumer objektumot és egy oAuthSignatureMethod_HMAC_SHA1 osztályt, és meghívjuk ez utóbbi  check_signature metódusát, mely le fogja ellenőrizni, hogy az oauth_signature GET paraméterben megfelelő érték jött-e. Végül ha igen, akkor ahogy az előbb is, visszaküldjük a kapott paramétert egy “Hello” előtaggal, ha nem, akkor megmondjuk hogy rossz volt a kérés.

A kulcs nevének érdemes egy a kulcs használóját, funkcióját beazonosító karaktersorozatot választani, például “example.com”, “example.com/app1″, de ezek nem kötöttségek, csak javaslatok.

A funkció használatához jellemzően elegendő lehet ennyi ismeret, de célszerű lehet utána olvasni az oAuth-nak, a kriptográfiának általában, a nyílt kulcsú titkosításnak, az RSA eljárásnak és az egyéb kapcsolódó témaköröknek, hogy tudjuk pontosan mit használunk, és hogyan működik.