-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



martedì 31 dicembre 2013

QR Code Map

Con lo sviluppo del mercato degli smartphone il QR (Quick Response) Code ha acquisito notorietà visto la facilità di poterli decodificare utilizzando la fotocamera del device. 
Uno tra le principali library che permette di generare e decodificare anche il QR Code è ZXing.NET.
Poichè nel crittogramma possiamo codificare informazioni, possiamo inventarci innumerevoli funzionalità da trasmettere agli smartphone: da semplici url con parametri nel querystring ad informazioni gestite opportunamente dalle app installate sugli smartphone.
In questo esempio la mappa sul tablet è sincronizzata agli spostamenti della mappa a monitor perchè il QR Code è generato dinamicamente ad ogni cambio extent e nel QR Code  è memorizzato il corrente extent della mappa; la fotocamera decodifica il QR Code ed applica il nuovo extent alla mappa sul tablet. In questo caso è stata applicata una compressione all'informazione per semplificare il crittogramma e rendere ancora più veloce la decodifica.
Ma vediamo una semplice Proof of concept che codifica un indirizzo di mappa. In questo caso potremmo utilizzare l'indirizzo del Map Viewer arcgis.com che accetta parametri via url. Nel caso specifico passeremo l'extent (parametro extent) e la web map da visualizzare (parametro webmap) e restituiremo un url abbreviato appoggiandoci ad un servizio di URL shortening (Goo.gl, bit.ly ecc.) o creandoci il nostro utilizzando ad esempio URL Rewrite Module di IIS così da avere un crittogramma semplificato.


        /// <summary>
        /// handle qr Code
        /// </summary>
        /// <param name="boundVariables">bound Variables</param>
        /// <param name="operationInput">object operationInput</param>
        /// <param name="outputFormat">object outputFormat</param>
        /// <param name="requestProperties">object requestProperties</param>
        /// <param name="responseProperties">object responseProperties</param>
        /// <returns>return handle rest request</returns>
        private byte[] QrCodeOperatorHandler(NameValueCollection boundVariables, JsonObject operationInput, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = "{\"Content-Type\" : \"application/json\"}";
            string textValue;
            bool found = operationInput.TryGetString("text"out textValue);
            if (!found || string.IsNullOrEmpty(textValue))
            {
                throw new ArgumentNullException("text");
            }
 
            bool useShortenerUrl = true;
            bool? useShortenerUrlValue;
            found = operationInput.TryGetAsBoolean("useShortenerUrl"out useShortenerUrlValue);
            if (found && useShortenerUrlValue.HasValue)
            {
                useShortenerUrl = useShortenerUrlValue.Value;
            }
 
 
            try
            {
                if ((outputFormat == "json") || (outputFormat == "image"))
                {
                    if (useShortenerUrl)
                    {
                        if (!ShortenerUrl.CheckUrl(textValue))
                        {
                            throw new QRCoderException("url wrong!");
                        }
 
                        ShortUrl shortUrl = new ShortUrl();
 
                        shortUrl.OriginalUrl = textValue;
                        
                        shortUrl.CodeShortUrl = ShortenerUrl.UniqueShortUrl(shortUrl.PrefixShortUrl);
                        shortUrl.Expire = true;
                        shortUrl.ShortenedUrl = null;
                        ShortenerUrl.AddUrlToDatabase(ref shortUrl);
 
                        textValue = ShortenerUrl.PublicShortUrl(this.shortenerUrl, shortUrl.ShortenedUrl);
                    }
 
                    QRCodeGenerator qrGenerator = new QRCodeGenerator();
                    QRCodeGenerator.QRCode qrCode = qrGenerator.CreateQrCode(textValue, QRCodeGenerator.ECCLevel.Q);
 
                    using (System.Drawing.Bitmap image = qrCode.GetGraphic(20))
                    {
                        if (outputFormat == "json")
                        {
                            responseProperties = "{\"Content-Type\" : \"application/json\"}";
                            string fileName = System.IO.Path.ChangeExtension(string.Format("_ags_{0}", Guid.NewGuid().ToString()), "png");
                            string pathfileName = System.IO.Path.Combine(this.pathOutputAGS, fileName);
                            image.Save(pathfileName, System.Drawing.Imaging.ImageFormat.Png);
                            JsonObject jsonObject = new JsonObject();
                            jsonObject.AddString("url"string.Format("{0}/{1}"this.pathOutputVirtualAGS, fileName));
                            jsonObject.AddLong("status_code", 200);
                            jsonObject.AddString("status_txt""OK");
                            jsonObject.AddString("text", textValue);
                            return Encoding.UTF8.GetBytes(jsonObject.ToJson());
                        }
                        else if (outputFormat == "image")
                        {
                            responseProperties = "{\"Content-Type\" : \"image/png\"}";
                            return Helper.ImageToByte(image, System.Drawing.Imaging.ImageFormat.Png);
                        }
                        else
                        {
                            throw new QRCoderException("Format output not found!");
                        }
                    }
                }
                else
                {
                    throw new QRCoderException("Format output not found!");
                }
            }
            catch
            {
                JsonObject jsonObject = new JsonObject();
                jsonObject.AddLong("status_code", 503);
                jsonObject.AddString("status_txt""UNKNOWN_ERROR");
                
                return Encoding.UTF8.GetBytes(jsonObject.ToJson());
            }
        }
Qui possiamo vedere la soe online.

Richiamiamo la soe per generare qr code dinamici da un template js arcgis:


                             require(["esri/lang""esri/request"], lang.hitch(thisfunction (esriLang, esriRequest) {
                                    this.map.on("extent-change", lang.hitch(thisfunction () {
                                        extent = this.map.geographicExtent;
                                        var params = { f: "json",
                                            text: esriLang.substitute({host: this.config.sharinghost, webmap: this.config.webmap, xmin: extent.xmin, ymin: extent.ymin, xmax: extent.xmax, ymax: extent.ymax }, "${host}/home/webmap/viewer.html?webmap=${webmap}&extent=${xmin},${ymin},${xmax},${ymax}"),
                                            useShortenerUrl: true
                                        };

                                        var requestHandle = esriRequest({
                                            url: this.config.qrcodeSOE,
                                            content: params,
                                            callbackParamName: "callback"
                                        }, { useProxy: false });

                                        requestHandle.then(
                                            function (response) {
                                                var qrcodeImg = dom.byId("qrcodeImg");
                                                var qrcodeAnchor = dom.byId("qrcodeAnchor");
                                                if (response.status_code === 200) {
                                                    qrcodeImg.src = response.url;
                                                    qrcodeAnchor.href = response.text;
                                                }
                                                else {
                                                    console.log("Error: ", response.status_txt);
                                                }
                                            }, function (error) {
                                                console.log("Error: ", error.message);
                                            });
                                    }));

                                }));


Qui vediamo l'esempio online.

sabato 30 novembre 2013

Servizio WCTS del PCN

Sin dalla versione 10.1, ArcGIS Server permette di esporre un servizio arcgis server di geoprocessing come WPS (Web Processing Service) ovvero uno standard OGC per esporre modelli di geoprocessing come servizio web mediante un'interfaccia standardizzata che facilita la pubblicazione e l'utilizzo dei 'processi' da parte dei client.
Ad oggi ArcGIS non dispone di un client per poter 'consumare' i servizi WPS ma, essendo servizi che utilizzano standard di comunicazione, possiamo effettuare direttamente le chiamate in HTTP GET o in HTTP POST o mediante messaggi SOAP.
Come esempio di prova possiamo prendere in considerazione il servizio WCTS del PCN (Portale Cartografico Nazionale) cioè il servizio web di trasformazione delle coordinate realizzato come Application Profile di WPS. Il servizio utilizza i grigliati IGM e copre tutto il territorio nazionale.

Le operazioni messe a disposizione del WPS sono: getCapabilities, DescribeProcess ed Execute.

L'operazione di getCapabilities fornisce l'accesso ad informazioni generali su un'implementazione diretta del WPS ed elenca le operazioni e i metodi di accesso supportati da tale implementazione. L'implementazione deve supportare l'operazione di getCapabilities via HTTP GET mentre il supporto HTTP POST è opzionale.

Via HTTP GET possiamo fornire i parametri di richiesta come KVP (Key Value Pair) codificate. Nel nostro esempio (ho inserito solo le KVP obbligatorie):
http://wms.pcn.minambiente.it/wps?service=wps&request=getCapabilities

O via POST:
  
                     XElement root = new XElement(
                        Global.wps + Enum.GetName(typeof(RequestWPS), RequestWPS.getCapabilities),
                        new XAttribute(XNamespace.Xmlns + "ows"Global.ows.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "wps"Global.wps.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "xlink"Global.xlink.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "xsi"Global.xsi.NamespaceName),
                        new XAttribute(Global.xsi + "schemaLocation"Global.schemaLocation.NamespaceName),
                        new XAttribute("language""en"),
                        new XAttribute("service"Global.serviceWPS),
                        new XElement(Global.wps + "AcceptVersions"new XElement(Global.ows + "Version"Global.versionWPS)));
 
                    XDocument xDoc = new XDocument(new XDeclaration(Global.versionXML, Encoding.UTF8.WebName, null), root);
 
                    using (var mem = new MemoryStream())
                    using (var writer = new XmlTextWriter(mem, Encoding.UTF8))
                    {
                        writer.Formatting = Formatting.Indented;
                        xDoc.WriteTo(writer);
                        writer.Flush();
                        mem.Flush();
                        mem.Seek(0, SeekOrigin.Begin);
                        using (var reader = new StreamReader(mem))
                        {
                            string xml = reader.ReadToEnd();
                            XDocument xDocument = this.ResponseWCTSPCN(xml);
 
                            var result = xDocument.Elements().Where(k => (k.Name == Global.wps + "Capabilities")).Elements().Where(h => (h.Name == Global.wps + "ProcessOfferings")).Elements().Where(x => (x.Name == Global.wps + "Process"));
 
                            foreach (var e in result)
                            {
                                if (e.Elements().Where(x => (x.Name == Global.ows + "Identifier") && (x.Value == this.wctsPCNIdentifier)).Count() == 1)
                                {
                                    jsonObject.AddString("title", e.Elements().Where(x => (x.Name == Global.ows + "Title")).Select(x => x.Value).FirstOrDefault());
                                    jsonObject.AddString("abstract", e.Elements().Where(x => (x.Name == Global.ows + "Abstract")).Select(x => x.Value).FirstOrDefault());
                                    break;
                                }
                            }
                        }
                    }
        /// <summary>
        /// return response wps pcn
        /// </summary>
        /// <param name="request">request wps</param>
        /// <returns>object XDocument</returns>
        private XDocument ResponseWCTSPCN(string request)
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(this.wctsPCNUrl);
            webRequest.Method = WebRequestMethods.Http.Post;
            byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(request);
            webRequest.ContentLength = requestBytes.Length;
            webRequest.ContentType = "text/xml;charset=utf-8";
 
            using (Stream streamWriter = webRequest.GetRequestStream())
            {
                streamWriter.Write(requestBytes, 0, requestBytes.Length);
                HttpWebResponse response = null;
                try
                {
                    response = (HttpWebResponse)webRequest.GetResponse();
                    Console.WriteLine(response.StatusDescription);
 
                    using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
                    {
                        TextReader textReader = new StringReader(streamReader.ReadToEnd());
                        return XDocument.Load(textReader);
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }
        }

Qui potete vederla live

L'operazione DescribeProcess consente al client WPS di richiedere una descrizione completa di uno o più processi che possono essere eseguiti dal servizio. Questa descrizione include l'input e i parametri di output e i formati e può essere utilizzata per creare automaticamente un'interfaccia utente per acquisire i valori di parametro da utilizzare per eseguire un processo.
Come per il getCapabilities l'implementazione deve supportare l'operazione di DescribeProcess via HTTP GET mentre il supporto HTTP POST è opzionale.

Via HTTP GET possiamo fornire i parametri di richiesta come KVP (Key Value Pair) codificate. Nel nostro esempio (ho inserito solo le KVP obbligatorie):
http://wms.pcn.minambiente.it/wps?service=wps&request=DescribeProcess&version=1.0.0&identifier=TransformCoordinates
oppure
http://wms.pcn.minambiente.it/wps?service=wps&request=DescribeProcess&version=1.0.0&identifier=ALL

Con il primo desideriamo visualizzare il processo nominato TransformCoordinates mentre utilizzando ALL desideriamo listare tutti i processi. Comunque nel caso specifico ne abbiamo solo uno (TransformCoordinates).

Qui potete vederla live

Infine l'operazione di Execute permette al client WPS di eseguire un processo specifico implementato dal server, utilizzando i valori di parametro di input forniti e restituendo i valori di output prodotti. Gli Input possono essere inclusi direttamente nella richiesta Execute, o mediante riferimenti web.
Per l'operazione di Execute l'implementazione dell'HTTP POST è obbligatoria mentre è facoltativa quella dell'HTTP GET.
I parametri di ingresso sono l'Identifier (TransformCoordinate), il SourceCRS, il TargetCRS e l'InputData. Per il SourceCRS e TargetCRS occorre visualizzare l'elenco fornito dalla richiesta di DescribeProcess mentre per l'InputData occorre passare i dati in formato GML nelle versioni supportate indicate sempre nella richiesta di DescribeProcess.


                   XElement root = new XElement(
                        Global.wps + Enum.GetName(typeof(RequestWPS), RequestWPS.Execute),
                        new XAttribute(XNamespace.Xmlns + "ows"Global.ows.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "wps"Global.wps.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "xlink"Global.xlink.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "xsi"Global.xsi.NamespaceName),
                        new XAttribute(Global.xsi + "schemaLocation"Global.schemaLocation.NamespaceName),
                        new XAttribute(XNamespace.Xmlns + "gml"Global.gml.NamespaceName),
                        new XAttribute("service"Global.serviceWPS),
                        new XAttribute("version"Global.versionWPS),
                        new XElement(Global.ows + "Identifier"this.wctsPCNIdentifier),
                        new XElement(Global.wps + "DataInputs"new XElement(Global.wps + "Input"new XElement(Global.ows + "Identifier""SourceCRS"), new XElement(Global.wps + "Data"new XElement(Global.wps + "LiteralData", sourceCRSValue))), new XElement(Global.wps + "Input"new XElement(Global.ows + "Identifier""TargetCRS"), new XElement(Global.wps + "Data"new XElement(Global.wps + "LiteralData", targetCRSValue))), new XElement(Global.wps + "Input"new XElement(Global.ows + "Identifier""TestTransformation"), new XElement(Global.wps + "Data"new XElement(Global.wps + "LiteralData""false"))), new XElement(Global.wps + "Input"new XElement(Global.ows + "Identifier""InputData"), new XElement(Global.wps + "Data"new XElement(Global.wps + "ComplexData"new XAttribute("mimeType""text/xml; subtype=gml/3.1.1"), new XElement(Global.gml + "MultiGeometry"new XAttribute("srsName", sourceCRSValue)))))),
                        new XElement(Global.wps + "ResponseForm"new XElement(Global.wps + "RawDataOutput"new XAttribute("mimeType""text/xml; subtype=gml/3.1.1"), new XElement(Global.ows + "Identifier""TransformedData"))));
 
                    XDocument xDoc = new XDocument(new XDeclaration(Global.versionXML, Encoding.UTF8.WebName, null), root);
 
                    XElement xElementMultiGeometry = xDoc.Descendants(Global.gml + "MultiGeometry").First();
 
                    foreach (IGeometry geometry in geometries)
                    {
                        if (geometry is IPoint)
                        {
                            IPoint point = geometry as IPoint;
                            xElementMultiGeometry.Add(new XElement(Global.gml + "geometryMember"new XElement(Global.gml + "Point"new XAttribute("srsName", sourceCRSValue), new XElement(Global.gml + "coordinates"string.Format("{0},{1}", point.X.ToString(Global.CultureUS), point.Y.ToString(Global.CultureUS))))));
                        }
                    }
 
                    using (var mem = new MemoryStream())
                    using (var writer = new XmlTextWriter(mem, Encoding.UTF8))
                    {
                        writer.Formatting = Formatting.Indented;
                        xDoc.WriteTo(writer);
                        writer.Flush();
                        mem.Flush();
                        mem.Seek(0, SeekOrigin.Begin);
                        using (var reader = new StreamReader(mem))
                        {
                            string xml = reader.ReadToEnd();
                            XDocument xDocument = this.ResponseWCTSPCN(xml);
 
                            var result = xDocument.Elements().Where(k => (k.Name == Global.gml + "MultiGeometry")).Elements().Where(h => (h.Name == Global.gml + "geometryMember"));
 
                            IList<IPoint> points = new List<IPoint>();
                            foreach (var e in result)
                            {
                                XElement xElement = e.Elements().Where(x => (x.Name == Global.gml + "Point")).ElementAtOrDefault(0);
                                string[] xy = xElement.Value.Split(new char[] { ' ' });
                                IPoint point = new PointClass();
                                point.PutCoords(Convert.ToDouble(xy[0], Global.CultureUS), Convert.ToDouble(xy[1], Global.CultureUS));
                                points.Add(point);
                            }
...


Qui potete vederla live mentre qui potete vedere l'help.





giovedì 31 ottobre 2013

OAuth 2

Da marzo 2013 ArcGIS Online ha introdotto nuove ArcGIS API basate su OAuth 2 per la gestione delle login degli utenti e delle app; OAuth 2 è disponibile per ArcGIS Online mentre non ancora per Portal for ArcGIS.
Ma cos'è OAuth? E' un protocollo di autorizzazione descritto da uno standard aperto che permette ad applicazioni di chiamare in modo sicuro ed autorizzato API messe a disposizione da un servizio Web. Permette di accedere alle risorse protette di un utente senza che esso debba condividere le sue credenziali (username e password). Qui possiamo vedere le specifiche mentre di seguito descriveremo l'implementazione in ArcGIS.
Come sviluppatori, utilizzando la piattaforma ArcGIS possiamo sviluppare i due seguenti tipi di applicazioni:
  • Applicazioni destinate ad utenti finali della piattaforma ArcGIS. Queste applicazioni necessitano di consentire agli utenti di accedere alla piattaforma tramite applicazione. Questi tipi di login sono conosciuti come user login;
  • applicazioni destinate ad utenti finali che non sono conosciuti alla piattaforma ArcGIS. Queste applicazioni necessitano di accedere alla piattaforma per conto dell'applicazione. Questo tipo di login sono conosciute come app login.
Possiamo sviluppare queste app utilizzando Javascript, iOS, Android, Flex e Silverlight. In questo contesto la piattaforma è ArcGIS Online che è disponibile in arcgis.com o utilizzando un ArcGIS Portal disponibile ad uno specifico portal url insieme a tutti i servizi associati.

Applicazioni basate su OAuth 2

Tutte le app che utilizzano OAuth 2 devono essere registrate con la piattaforma ed avere dalla piattaforma assegnato un AppID. Per registrare la nostra applicazione, una volta che accediamo alla piattaforma, utilizzando il nostro account di sviluppatore o dell'organizzazione utilizziamo la funzione 'Aggiungi elemento' in 'I miei contenuti' per aggiungere e registrare la nostra app.


La registrazione assegna all'applicazione un AppID e un App Secret opzionale.

User login

User login utilizzano le ArcGIS API basate su OAuth 2 e sono applicazioni che guidano l'utente ad accedere alla piattaforma tramite pagina di login ospitata nella piattaforma ArcGIS.
Questo è comune a tutti i tipi di app: web app su browser, web app server side, app su device o tablet e app desktop. I device, tablet e desktop utilizzeranno controlli browser lato client per integrare questa login all'interno dell'app. L'applicazione riceverà un token di accesso utente che può utilizzare per conto dell'utente per accedere alla piattaforma. Il token di accesso necessita di essere inviato alla piattaforma per tutte le richieste. Utilizzando gli SDK client ArcGIS, l'Identity Manager si prenderà cura di effettuare tutte le richieste con il token mentre lavorando direttamente con le REST API sarà responsabilità dello sviluppatore includere il token per ogni richiesta.
In funzione del tipo di applicazione ci sono delle leggere differenze:

User login in app javascript

Applicazioni su browser devono registrare uno o più URI redirect quando vengono registrate.


Questo è l'URI dell'app e al quale sarà restituito il token di accesso.
La user login è effettuata in un singolo passaggio e richiede che l'app indirizzi il browser all'url di autorizzazione OAuth 2 del portale:

https://www.arcgis.com/sharing/oauth2/authorize?
client_id=APPID&
response_type=token&
redirect_uri=<redirect_uri>

Questo unico passaggio è riferito come una OAuth a concessione implicita. Il redirect_uri passato dalla app in questa richiesta deve essere una superstringa di una delle uri registrate per l'app.

Se l'utente finale accede con successo alla piattaforma, il server restituisce un token di accesso reindirizzando il browser allo specifico redirect_uri. Il token di accesso è restituito come parte dell'url accodato al redirect _uri.

Ad esempio, il server può reindirizzare il browser al seguente indirizzo URL:

http://app.example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&expires_in=3600

Il token di default è valido per due ore. E' possibile richiedere con questo metodo un token di accesso con un periodo di validità più lungo (in minuti). Il token restituito può essere valido per un periodo più breve in base al periodo massimo di validità impostato dall'organizzazione dell'utente o dalla piattaforma.

Qui potete vedere un esempio utilizzando le ArcGIS API for Javascript.

User login con app iOS, Android e WPF

In questo caso è raccomandato un flusso in due passaggi indicato come concessione del codice di autorizzazione.

Registrazione

Come parte del processo di registrazione, l' app registra un register_uri. Il register_uri può essere o uno speciale valore urn:ietf:wg:oauth:2.0:oob  od uno specifico URI personalizzato per l'applicazione che è gestito dal device. La piattaforma risolve lo speciale URI ad un URL ospitato sulla piattaforma (arcgis.com o portal) che può essere utilizzato dall'applicazione installata per ottenere il codice di autorizzazione alla fine del primo passaggio di autenticazione dell'utente e l'autorizzazione dell'applicazione.

Applicazioni iOS e Android possono anche registrare un redirect_uri personalizzato che il browser
risolve ad un gestore dell'app eseguita sul device. Un esempio di redirect_uri è x-com.mycorp.myapp://oauth.callback. In questo caso il browser direttamente chiama il gestore dell'applicazione alla fine della login user.

La user login viene eseguita in due passaggi: il primo restituisce un codice di autorizzazione e il secondo restituisce il token di accesso.

Codice di autorizzazione

Il primo passaggio dell'autenticazione dell'utente è per l'app ottenere un codice di autorizzazione per conto dell'utente. Questo richiede che l'app apra un finestra del browser e indirizzi l'utente al seguente indirizzo:

https://www.arcgis.com/sharing/oauth2/authorize?
client_id=APPID&
response_type=code&
redirect_uri=<redirect_uri>
Il redirect_uri passato è o una speciale stringa (urn:ietf:wg:oauth:2.0:oob) per il redirect_uri dell'ArcGIS ospitato o un URI registrato personalizzato dall'app sul device.

Se l'utente con successo presenta le proprie credenziali (username e passowrd) all' authorization server (arcgis.com o un portal) e se l'utente accetta l'identità registrata dell'applicazione corrispondete al client_id, il server restituisce un codice di autorizzazione reindirizzando il browser allo specificato redirect_uri con il codice di autorizzazione aggiunto come un parametro della query string. 

Se il valore passato  per il redirect_uri è urn:ietf:wg:oauth:2.0:oob, l'authorization server (arcgis.com o un portal) reindirizza il browser a https://www.arcgis.com/sharing/oauth2/approval  o al portal con il codice di autorizzazione disponibile all'applicazione nel titolo della pagina. Per esempio il server può reindirizzare il browser al seguente URL:

https://www.arcgis.com/sharing/oauth2/approval?code=SplxlOBeZQQYbYS6WxSbIA

Questo speciale URL restituisce il contenuto HTML del titolo che avrà la seguente forma:

<title>SUCCESS code=Sp1x1OBeZQQYbYS6WxSbIA</title>
Spetta all'applicazione che ospita il browser estrarre il codice di autorizzazione dal titolo e chiudere la finestra del browser se il codice non deve essere visualizzato all'utente finale.

Se il valore passato al redirect_uri è un URI registrato personalizzato sul device e gestito dall'applicazione, il gestore dell'applicazione è responsabile di ricevere il redirect_uri dal browser e estrarre il codice di autorizzazione dalla query string dell'URL. Ad esempio il server può reindirizzare il browser al seguente URL:

x-com.mycorp.myapp://oauth.callback?code=SplxlOBeZQQYbYS6WxSbIA

Qui potete vedere un esempio con iOS mentre qui potete vedere un esempio con WPF. Per Android potete invece vedere qui: il supporto completo con Android sarà con la versione 10.2 dell'SDK.

Token di accesso

Una volta che il codice di autorizzazione è stato ottenuto, l'app deve scambiarlo per un token di accesso. La richiesta è una richiesta POST all'endpoint del token:

https://www.arcgis.com/sharing/oauth2/token

e tutti i parametri (nel seguente esempio) devono essere inviati nel corpo della richiesta e non come parte della componente query dell'URI:

client_id=APPID&
client_secret=APPSECRET&
grant_type=authorization_code&
code=CODICEDIAUTORIZZAZIONE

La risposta è restituita come oggetto JSON ed include un campo access_token. L'app deve utilizzare questo token quando farà le richieste successive al server. Un esempio di risposta JSON:

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "expires_in": 3600,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"    
}

L'uso dell'APPSECRET (ouath2 client_secret) in questa richiesta è opzionale nel caso di user login

L'autenticazione dell'app dalla piattaforma durante la user login è basata sull'accettazione dell'identità dell'app corrispondente all'APPID visualizzata dall'utente.

I token di accesso sono di breve durata. L'app può prendere un nuovo access_token utilizzando il refresh_token ottenuto precedentemente. La durata del refresh token che è restituita da questa chiamata è controllabile dall'app. Il tempo predefinito di durata per il token aggiornato restituito da questo flusso è di due settimane.
Utilizzando questo flusso puoi richiedere un refresh token che è valido per un periodo più lungo.
Il refresh token che è restituito può essere valido per un periodo più breve di quello della richiesta perché comunque dipende dal massimo tempo di validità impostato a livello dell' organizzazione dell'utente o piattaforma.

Puoi scambiare un valido refresh_token  per un access_token utilizzando lo stesso token endpoint:

https://www.arcgis.com/sharing/oauth2/token

I parametri richiesti in questo caso sono il refresh_token precedentemente ottenuto e un grant_type di refresh_token:

client_id=APPID&
grant_type=refresh_token&
refresh_token=REFRESHTOKENOTTENUTONELPRECEDENTEPASSO

User login con PHP, JSP, ASP.NET o altre web app server side

Una web app server side è un'app dove l'utente interagisce con l'app tramite page web che sono visualizzate in un browser, ma la parte significativa della logica applicativa viene eseguita lato server.
Le web application server side devono registrare uno o più URI di reindirizzamento al momento della registrazione. Questo sarà l'URI dell'app e l'URI al quale sarà restituito il token di accesso dell'utente.
La user login è eseguita in due passi - il primo restituisce il codice di autorizzazione e il secondo restituisce il token di accesso.

Codice di autorizzazione
Il primo passo dell'autenticazione utente è per l'app ottenere un codice di autorizzazione per conto dell'utente. Questo richiede che l'app indirizzi l'utente all'URL di autorizzazione OAuth 2 del portale (in questo esempio è mostrato arcgis.com):

https://www.arcgis.com/sharing/oauth2/authorize?
client_id=APPID&
response_type=code&
redirect_uri=<redirect_uri>

Se l'utente con successo presenta le credenziali (ad esempio username e password) al server di autorizzazione (arcgis.com) e se l'utente accetta l'identità registrata dell'applicazione, il server restituisce un codice di autorizzazione reindirizzando il browser allo specifico redirect_uri utilizzando una HTTP response redirect  allo specifico redirect_uri. Il codice di autorizzazione è messo a disposizione come parametro della query string e può essere accessibile dall'applicazione server-side che esegue il redirect_uri.
Ad esempio il server può reindirizzare il browser al seguente URL:

https://app.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA

L'applicazione in esecuzione a questo URL poi fa una seconda richiesta server side per ottenere un token di accesso in cambio del codice di autorizzazione come descritto nella seguente sezione.

Token di accesso

Una volta che il codice di autorizzazione è stato ottenuto, l'app ha bisogno di scambiarlo per un token di accesso.
La richiesta è una richiesta POST all'endpoint /token del portale, qui mostrato per arcgis.com:

https://www.arcgis.com/sharing/oauth2/token

Tutti i parametri (nel seguente esempio) devono essere inviati nel corpo della richiesta e non come parte della query string dell'URI:

client_id=APPID&
client_secret=APPSECRET&
grant_type=authorization_code&
code=CODICEOTTENUTONELPRECEDENTEPASSO

La risposta è restituita come un oggetto JSON e include un campo access_token. L'app deve utilizzare questo token quando farà le successive richieste per accedere alle risorse.

Un esempio di risposta JSON è come la seguente:

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"example",
   "expires_in":3600,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"    
}

Il refresh token può essere utilizzato con i successivi token di accesso. I token di accesso sono di breve durata. L'app può acquisire un nuovo access_token utilizzando il refresh_token ottenuto precedentemente.
La durata del refresh token restituito da questa chiamata è controllabile dall'app. Il valore di default di durata per il refresh token restituito da questo flusso è di due settimane. Utilizzando questo flusso puoi richiedere un refresh token che è valido per un periodo più lungo passando un parametro expiration (in minuti) durante l'autorizzazione. Il refresh token che è restituito può essere valido per un periodo più breve rispetto a quello richiesto perché comunque dipende dal massimo tempo di validità impostato a livello dell'organizzazione dell'utente o piattaforma.

Per acquisire un valido refresh_token per un access_token utilizzare lo stesso /token endpoint:

https://www.arcgis.com/sharing/oauth2/token

I parametri richiesti in questo caso sono il refresh_token precedentemente ottenuto e un grant_type uguale a refresh_token:

client_id=APPID&
grant_type=refresh_token&
refresh_token=REFRESHTOKENOTTENUTONELPRECEDENTEPASSO

Restituisce un access_token aggiornato e il refresh_token può essere successivamente utilizzato. Se il refresh_token non è più valido, verrà restituito una risposta di errore e l'app sarà tenuta a richiedere la user login nuovamente.

App login

Le applicazioni destinate ad utenti finali che non sono conosciuti dalla piattaforma ArcGIS utilizzano app login per connettersi alla piattaforma. In questo caso l'applicazione deve utilizzare sia un APPID (OAuth 2 client_id) e un APPSECRET (OAuth 2 client_secret). Sei responsabile di sviluppare l'applicazione in modo che mantenga segreto l'APPSECRET,  incluso anche il fatto che utenti 'malevoli' possano scaricare ed analizzare l'applicazione iOS e Android o visualizzare il codice dell'applicazione Javascript utilizzando strumenti di sviluppo. Un'applicazione 'malevole' che ha accesso alle credenziali dell'applicazione (APPID e APPSECRET) può accedere a servizi a pagamento su ArcGIS, che quindi sarà fatturato all'applicazione. Per la maggior parte delle applicazioni javascript, iOS e Android, questo implica il fatto che la app deve avere una componente applicativa server side che mantiene in sicurezza le credenziali applicative ed esegue il lavoro per conto dell'app.
La componente applicativa server side che ha accesso alle credenziali dell'applicazione può ottenere un token utilizzando una singola richiesta. Il tipo di grant OAuth 2 impostato è client_credentials. La richiesta di POST è fatta all'endpoint token OAuth 2.
 
Per arcgis.com:
https://www.arcgis.com/sharing/oauth2/token
 
Parametri:
client_id=APPID&
client_secret=APPSECRET&
grant_type=client_credentials
 
L'autenticazione eseguita con successo direttamente restituisce una risposta JSON contenente l'accesso token che permette all'applicazione di lavorare con le risorse che sono accessibili all'applicazione stessa (risorse che sono state condivise con l'applicazione). L'utilizzo del client_secret come precedentemente descritto è obbligatorio.
 
La componente applicativa server side che fa questa chiamata può essere un componente personalizzato che ha la sua propria API che 'wrappa' le API della piattaforma ArcGIS ed espone solo quelle funzioni che necessitano alla app.
 
La componente applicativa server side può anche essere un proxy che preserva le firme ArcGIS REST mentre inoltra le chiamate alle API della piattaforma ArcGIS. Questa è l'opzione che deve essere implementata se l'applicazione è sviluppata utilizzando le API client ArcGIS  che le firme REST.
 
In entrambi i casi le chiamate fatte dalla componente applicativa server side alla piattaforma ArcGIS  hanno bisogno di includere i token di accesso ottenuti dalla componente in cambio delle credenziali dell'applicazione utilizzando la grant client_credentials precedentemente descritta.
In entrambi i casi la componente applicativa server side anche ha bisogno di essere messa in sicurezza che solo l'applicazione possa accedere ad essa.
 
Qui si riporta delle possibili soluzioni per mettere in sicurezza la componente applicativa server-side:
  • Per applicazioni che hanno i loro propri utenti autenticati che rimangono sconosciuti alla piattaforma ArcGIS, l'applicazione può restringere l'accesso alla componente applicativa server side per sessioni di utenti autenticati dell'applicazione. Questo sottintende che gli utenti dell'app che sono sviluppatori non sono 'malevoli'. Se si vuole gestire questo caso e verificare l'uso improprio della componente applicativa server side, occorre monitorarli e controllarli;
  • applicazioni delle quali gli utenti sono anonimi anche all'applicazione si può restringere la componente applicativa server side ad utenti finali 'umani' utilizzando la tecnologia CAPTCHA. Ciò richiede che l'applicazione incorpori al suo interno CAPTCHA nella sua user experience;
  • le applicazioni possono anche limitare la funzionalità esposta dalla componente server side, ponendo restrizioni sull'IP sulla componente app server side e limiti di banda nella componente con valori appropriati. I limiti di banda sono effettivamente delle misure preventive di abuso della componente applicativa server side da codice malevole server side. 
Le tecniche descritte qui sono applicabili a Javascript, Flex e Silverlight come a iOS, Android e dispositivi client simili.
 

Applicazioni non basate su OAuth2

Sia ArcGIS Online che Portal for ArcGIS supportano la chiamata REST API generateToken che può essere utilizzata sia con le credenziali dell'utente ottenute dall'utente che si autentica nella piattaforma tramite l'applicazione o con  le credenziali proprie dell'applicazione.
La chiamata restituisce un token di accesso su una autenticazione avvenuta con successo che  poi utilizzerà nelle successive richieste.
La chiamata al generateToken deve essere fatta su HTTPS. Tutte le successive richieste che usa il token necessitano di essere fatte su HTTPS se il portale o l'organizzazione per accedere lo dovesse richiedere.
 

User login

Applicazioni che implementano user login basate sulla chiamata al generateToken sono responsabili di presentare all'utente finale una finestra di dialogo di login per le credenziali dell'utente. L'applicazione è responsabile di mantenere le credenziali dell'utente in sicurezza e trasmetterle su HTTPS.
 
E' comunque consigliabile utilizzare l'appropriato client SDK  per connettersi ed autenticarsi con ArcGIS Online piuttosto che fare chiamate dirette alle REST API. Eseguire connessioni e autenticazioni tramite SDK client ti libera dai dettagli dell'autenticazione così come la gestione delle credenziali dell'utente durante il processo di autenticazione. Un esempio è l'utilizzo dell'IdentityManager dijit che consente all'utente di accedere ai propri account ArcGIS Online o a Portal for ArcGIS, e le successive chiamate sono fatte automaticamente utilizzando il client session e esri.request automaticamente.
 
Queste sono le limitazioni di implementare la user login non su OAuth2:
  • L'identità dell'app rimane sconosciuta alla piattaforma;
  • gli utenti non possono accedere al provider di identità federata che sono accessibili tramite pagine di login ospitate nella piattaforma esposte via API OAuth 2 

App login  

Per l'utente che rappresenta l'applicazione deve essere fornito un nome utente (ad esempio, app-username) e password (ad esempio, app-password). App destinate ad utenti sconosciuti alla piattaforma possono accedere utilizzando queste app-username e app-password con la chiamata al generateToken. È responsabilità dell'app mantenere l'app-username e l'app-password in sicurezza utilizzando codice lato server o con un flusso lato server.
 
Le limitazione di implementazione di app login in questo modo sono le seguenti:
  • L'identità dell'app è modellata tramite un utente surrogato;
  • non c'è una chiara separazione degli utenti dalle app nella piattaforma.
 
 

Lavorare con i portal ArcGIS

Gli esistenti portal come già detto non supportano OAuth 2. Supportano una singola chiamata a generateToken delle API che restituisce il token di accesso.
Pertanto le applicazioni dovrebbero continuare ad utilizzare il modello di autenticazione delle applicazioni non basate su OAuth 2 sia per le user login che per le app login.
Come già detto il modo consigliato è quello di utilizzare l'appropriato Identity Manager dell'SDK.
Una volta che è stato ottenuto un token di accesso per il portal, l'applicazione può ottenere un token di accesso per i server federati con il portal utilizzando la richiesta generateToken con il parametro serverURL.
Se il portal in oggetto utilizza per la sicurezza HTTP, Windows Integrated o basata su PKI anziché utilizzare l'autenticazione basata su token, la risposta alla richiesta di autenticazione del server necessita di essere gestita utilizzando lo stack di comunicazione nativa della piattaforma client.
 

 

Lavorare con gli ArcGIS Server

 
Server ArcGIS che non sono affiliati con ArcGIS Online o Portal for ArcGIS non supportano OAuth 2; supportano una singola chiamata a generateToken delle API che restituisce il token di accesso.
Le applicazioni dovrebbero continuare ad utilizzare il modello di autenticazione delle applicazioni non basate su OAuth 2 sia per le user login che per le app login. 
Come già detto il modo consigliato è quello di utilizzare l'appropriato Identity Manager dell'SDK.
Se il server in oggetto utilizza per la sicurezza HTTP, Windows Integrated o basata su PKI anziché utilizzare l'autenticazione basata su token, la risposta alla richiesta di autenticazione del server necessita di essere gestita utilizzando lo stack di comunicazione nativa della piattaforma client.