2015-02-05 8 views
9

Sto cercando di creare un servizio daemon Ruby per accedere all'API di riposo di Office 365. Recentemente è stato reso possibile farlo tramite il flusso "client_credentials" di OAuth, come dettagliato in questo post del blog: http://blogs.msdn.com/b/exchangedev/archive/2015/01/22/building-demon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow.aspxAPI di Office 365 Rest - autenticazione settimana Daemon

Non riesco a generare un token di accesso valido. L'endpoint Token mi restituisce un JWT tuttavia quando si utilizza questo token ho ricevuto una 401 con questo messaggio:

The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2

Capisco che il flusso client_credentials richiede di presentare un cert X.509, purtroppo tutti gli esempi presenti nel blog i post sono per C#.

Sto utilizzando un certificato autofirmato generato e una chiave privata per eseguire un'asserzione del client quando si richiede il token. Ho seguito i passaggi nel post del blog per generare il certificato e aggiornare il manifest per utilizzare questo certificato.

Questo è il codice rubino per riferimento:

def request_token 
    uri = URI.parse("https://login.windows.net/== TENANT-ID ==/oauth2/token?api-version=1.0") 
    https = Net::HTTP.new(uri.host, uri.port) 

    req = Net::HTTP::Post.new(uri.request_uri) 
    req.set_form_data(
    :grant_type => 'client_credentials', 
    :redirect_uri => 'http://spready.dev', 
    :resource  => 'https://outlook.office365.com/', 
    :client_id  => '== Client ID ==', 
    :client_secret => '== Client secret ==' 
) 

    https.use_ssl = true 
    https.cert = client_cert 
    https.key = client_key 
    https.verify_mode = OpenSSL::SSL::VERIFY_PEER 

    resp = https.start { |cx| cx.request(req) } 

    @access_token = JSON.parse(resp.body) 
end 

Ovviamente ho rimosso alcuni bit di informazione per la sicurezza. Anche se è rubino, puoi vedere che sto usando il mio certificato per convalidare il client utilizzando una connessione SSL.

Ecco alcuni di più informazioni sull'errore:

"x-ms-diagnostics" => "2000010; 
    reason=\"The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2.\"; 
    error_category=\"insufficient_auth_strength\"", 
"x-diaginfo"=>"AM3PR01MB0662", 
"x-beserver"=>"AM3PR01MB0662" 

Qualsiasi aiuto sarebbe apprezzato.


Modifica

Per gli altri che cercano di fare qualcosa di simile in Ruby ecco una Sintesi del codice che uso: https://gist.github.com/NGMarmaduke/a088943edbe4e703129d

L'esempio utilizza un ambiente Rails ma dovrebbe essere abbastanza facile da rimuovere i bit specifici di Rails.

Ricordarsi di sostituire l'ID CLIENT, TENANT_ID e CERT_THUMBPRINT con i valori corretti e indirizzare il percorso certificato ei metodi della chiave client sul percorso file corretto.

Poi si può fare qualcosa di simile:

mailbox = OfficeAPI.new("[email protected]") 
messages = mailbox.request_messages 
+0

Ehi @Nick hai mai ottenuto questo lavoro? Mi piacerebbe vedere il tuo codice. Sto cercando di fare qualcosa di simile in Ruby e non riesco a farlo funzionare anche dopo aver provato a fare ciò che la risposta di Jason ha detto di fare. –

+0

Hey Joel, ho appena aggiunto qualche altro dettaglio alla domanda, con una nota del mio codice –

+0

Perfetto. Grazie mille! –

risposta

9

Invece di un client_secret nel vostro corpo della richiesta, è necessario un client_assertion. Questo è un po 'più complesso, ma è la ragione per cui hai bisogno di quel certificato.

Fondamentalmente è necessario creare un token Web JSON e firmarlo con il proprio certificato utilizzando un hash SHA256. Il token è andare a guardare qualcosa di simile:

Intestazione:

{ 
    "alg": "RS256", 
    "x5t": "..." // THUMBPRINT of Cert 
} 

Carico utile:

{ 
    "aud": "https:\\/\\/login.windows.net\\/<The logged in user's tenant ID>\\/oauth2\\/token", 
    "exp": 1423168488, 
    "iss": "YOUR CLIENT ID", 
    "jti": "SOME GUID YOU ASSIGN", 
    "nbf": 1423167888, 
    "sub": "YOUR CLIENT ID" 
} 

Se siete ancora con me, è ora necessario Base64-encode entrambi i pezzi (separatamente), quindi concatenarli con un '.'.Quindi ora dovresti avere:

base64_header.base64_payload 

Ora prendi quella stringa e firmala con il tuo certificato, usando un hash SHA256. Poi Base64-codificare il risultato di ciò, url-encode, quindi aggiungere alla stringa, quindi ora avete:

base64_header.base64_payload.base64_signature 

Infine, includere questo nella vostra POST all'endpoint token come parametro client_assertion, e anche includere un parametro client_assertion_type impostato su "urn: ietf: params: OAuth: client-affermazione, tipo: JWT-portatore":

req.set_form_data(
    :grant_type => 'client_credentials', 
    :redirect_uri => 'http://spready.dev', 
    :resource  => 'https://outlook.office365.com/', 
    :client_id  => '== Client ID ==', 
    :client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', 
    :client_assertion => 'base64_header.base64_payload.base64_signature' 
) 

mi auguro che aiuta! Questo è tutto basato sulla mia ricerca su come lo fa ADAL, e non l'ho testato io stesso in Ruby.

+4

Perfetto, grazie ancora Jason. Sarebbe una buona idea documentarlo per persone che non si sviluppano con ADAL. È possibile aggiornare il documento delle credenziali del client di Azure AD qui: https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx –

+1

@NickMaher Tale documento deve essere definitivamente aggiornato. Ho fatto una richiesta a Microsoft su questo. –

+0

@Jason, una domanda: perché questo certificato è necessario quando si chiama l'API di Office365 ma non è necessario quando si chiama Graph API? Sono in grado di eseguire l'autenticazione con 'clientId' e' clientSecret' con le credenziali del client che concedono il flusso in Graph API, ma non in Office365. Perché? –

0

Solo alcune aggiunte: la rivendicazione del pubblico nell'asserzione è la stessa dell'endpoint che si indirizza alla richiesta del token. Come identificato correttamente da Jason, questo è l'endpoint del token di AAD: https://login.windows.net/ {l'utente che desidera un token dell'app per}/oauth2/token. Anche il nbf e l'exp sono il tempo in cui hai creato l'asserzione in un tempo epoche unix, ad es. in .net si farebbe qualcosa come "WebConvert.EpocTime (DateTime.UtcNow)". Per "non prima" (nbf) forse sottrarre un buffer per l'inclinazione dell'orologio, ad es. 5 minuti; e per scadere in (exp) aggiungere un po 'di tempo, ad es. 15 minuti (quindi l'affermazione rimane valida per quel tempo).

Ecco una traccia violinista di una richiesta di token (RAW): POST https://login.windows.net/0e49ef1f-ca07-45f1-b4c0-ac9409d3e576/oauth2/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded client-richiesta-id: a8108f88-275b-424d- ac28-f675aabe548e ritorno-client-richiesta-id: true x-client-SKU: .NET x-client-Ver: 2.12.0.0 x-client-CPU: x64 x-client-OS: Microsoft Windows NT 6.2.9200.0 Host: login.windows.net Content-Length: 983 Expect: 100-continuano Connection: Keep-Alive

01.235.164,106 mila

risorsa = https% 3A% 2F% 2Fgraph.windows.net% 2F & client_id = f17bb8a5-2bef-4ad5-a83f-cd7113449fc2 & client_assertion_type = urna% 3Aietf% 3Aparams% 3Aoauth% 3Aclient affermazione tipo% 3Ajwt-bearer & client_assertion = eyJhbGciOiJSUzI1NiIsIng1dCI6ImY4S2JVY0xtMnItS2s4b1Z3ZVZYTFU0NzhJcyJ9.eyJhdWQiOiJodHRwczpcL1wvbG9naW4ud2luZG93cy5uZXRcLzBlNDllZjFmLWNhMDctNDVmMS1iNGMwLWFjOTQwOWQzZTU3Nlwvb2F1dGgyXC90b2tlbiIsImV4cCI6MTQyMjk4NDMzNSwiaXNzIjoiZjE3YmI4YTUtMmJlZi00YWQ1LWE4M2YtY2Q3MTEzNDQ5ZmMyIiwianRpIjoiZTI3OTA5YTctZGYwMC00NjBhLTlmZjctOGZkNDExOWVmNTYzIiwibmJmIjoxNDIyOTgzNzM1LCJzdWIiOiJmMTdiYjhhNS0yYmVmLTRhZDUtYTgzZi1jZDcxMTM0NDlmYzIifQ.g9bo4-lxpNJ4kEOMuQxODU-5iakwSVIzyRQEPLdbpuNn_XD4lcvt2yBIWT12EQaUVKkMyqFrDiIh4Oav565-Po7HfhmSPF3URXVj8Kx5lx17Zh0nWiaNkRXEi1vhwswsfjm1o-8B8LGUJTtT6JXTognrueuSL1aEE_-4qSG1y74aoc949Un1pQCjwuBtao4vs4CPJLu9Y9mVbirVRRtiIfxkUMmzf6yfMtuhugoGmrvUYntUo4x 6N2fu4LxGjuIs7czyrMMAmDRo-XK4sAhDo5uof10HKb8ETEU8mhObwNZcz86MYHWbZm3Z_HDOwzC9kA_tp6hWqmlJ3c-gLg5VXA & grant_type = client_credentials

Spero che questo aiuti! Buona fortuna!

Matthias

+1

Nota: puoi vedere se la tua asserzione è corretta usando http://jwt.calebb.net e incolla il valore codificato base64url che generi/spedisci lì. Dovrebbe mostrare contenuti come nell'esempio precedente di Jason. –

4

Sono riuscito a farlo funzionare, quindi ho pensato di aggiungere un altro consiglio al mix.Tutti gli articoli di istruzione là fuori dicono che dovresti aggiungere il tuo certificato al file manifest. Ho avuto problemi con questo, ma qui è quello che ho fatto che finalmente ha reso il lavoro:

  • In Azure, vai a Impostazioni> certificati di gestione
  • Carica la chiave pubblica in un file .cer (google in giro se don so come convertirlo). Questo dovrebbe essere un file binario su cui si basa l'editor di testo.
  • Ora che è stato caricato, Microsoft ti darà l'identificazione personale. È nella colonna "Thumbprint". Ma, è in hex, non base64. Così, convertirla in questo modo:

    # Hint: use your actual thumbprint, not this fake one 
    echo '5292850026FADB09700E7D6C1BCB1CD1F3270BCC' | xxd -r -p | base64 
    
  • Infine, utilizzare questo Base64 identificazione codificato come il valore per x5t nell'intestazione JSON.

+0

Herm, vedo l'impronta digitale, ma la colonna non è abbastanza larga, quindi taglia corto e aggiunge ellissi. Nessun modo ovvio per espandere le informazioni ... Idee? – stu

+0

non importa, ho capito ... openssl x509 -in server.cer -fingerprint -noout | tr -d ':' | awk -F "=" '{print $ 2}' | xxd -r -p | Base64 – stu