ruby rest ssl office365 exchangewebservices

ruby - Office 365 Rest API: autenticación de la semana del demonio



ssl office365 (4)

Estoy tratando de crear un servicio Ruby Daemon para acceder a la API de descanso de Office 365. Recientemente fue posible hacer esto a través del flujo de ''credenciales_clientes'' de OAuth, como se detalla en esta publicación de 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.aspx

Estoy luchando por generar un token de acceso válido. El punto final del token me devuelve un JWT, sin embargo, al usar este token, recibí un 401 con este mensaje:

El token de acceso se adquiere utilizando un método de autenticación que es demasiado débil para permitir el acceso a esta aplicación. La fuerza de autenticación presentada fue 1, la requerida es 2

Entiendo que el flujo client_credentials requiere que presente un certificado X.509, desafortunadamente todos los ejemplos en la publicación del blog son para C #.

Estoy usando un certificado autofirmado generado y una clave privada para hacer una afirmación del cliente cuando solicito el token. Seguí los pasos en la publicación del blog para generar el certificado y actualizar el manifiesto para usar este certificado.

Este es el código ruby ​​para referencia:

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

Obviamente he eliminado ciertos bits de información por seguridad. Aunque es rubí, puede ver que estoy usando mi certificado para validar al cliente mediante una conexión SSL.

Aquí hay más información sobre el error:

"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"

Cualquier ayuda seria apreciada.

Editar

Para otros que buscan hacer algo similar en Ruby, aquí hay una descripción general del código que uso: https://gist.github.com/NGMarmaduke/a088943edbe4e703129d

El ejemplo utiliza un entorno Rails, pero debería ser bastante fácil quitar los bits específicos de Rails.

Recuerde reemplazar SU ID DE CLIENTE, TENANT_ID y CERT_THUMBPRINT con los valores correctos y apunte la ruta del certificado y los métodos de la clave del cliente a la ruta correcta del archivo.

Entonces puedes hacer algo como esto:

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



En lugar de un client_secret en su cuerpo de solicitud, necesita un client_assertion . Esto es un poco más complejo, pero es la razón por la que necesita ese certificado.

Básicamente, debe crear un token web JSON y firmarlo con su certificado utilizando un hash SHA256. El token se verá así:

Encabezamiento:

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

Carga útil:

{ "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" }

Si todavía está conmigo, ahora necesita codificar en base64 ambas piezas (por separado), luego concatenarlas con un ''.''. Entonces ahora deberías tener:

base64_header.base64_payload

Ahora toma esa cadena y la firma con su certificado, utilizando un hash SHA256. Luego codifique en base64 el resultado de eso, codifíquelo en url, luego añádalo a la cadena, así que ahora tiene:

base64_header.base64_payload.base64_signature

Finalmente, incluya esto en su POST al punto final del token como el parámetro client_assertion , y también incluya un parámetro client_assertion_type establecido en "urn: ietf: params: oauth: client-afirion-type: jwt-bearer":

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'' )

¡Espero que eso ayude! Todo esto se basa en mi investigación sobre cómo ADAL hace, y no lo he probado en Ruby.


Me las arreglé para hacer que esto funcionara, así que pensé en darle un consejo más a la mezcla. Todos los artículos de instrucciones por ahí dicen que debe agregar su certificado al archivo de manifiesto. Tuve problemas con eso, pero esto es lo que hice que finalmente lo hizo funcionar:

  • En Azure, vaya a Configuración> Certificados de administración
  • Suba la clave pública como un archivo .cer (busque en Google si no sabe cómo convertirla). Este debería ser un archivo binario en el que su editor de texto se muestre.
  • Ahora que está cargado, Microsoft le dará la huella digital. Está en la columna "Huella digital". Pero está en hexadecimal, no en base64. Entonces, conviértalo así:

    # Hint: use your actual thumbprint, not this fake one echo ''5292850026FADB09700E7D6C1BCB1CD1F3270BCC'' | xxd -r -p | base64

  • Finalmente, use esta huella digital codificada en base64 como el valor para x5t en el encabezado JSON.


Solo algunas adiciones: el reclamo del público en la afirmación es el mismo que el punto final al que se dirige con la solicitud de token. Como Jason identificó correctamente, este es el punto final del token de AAD: https://login.windows.net/ {el inquilino para el que desea un token de aplicación} / oauth2 / token. Además, nbf y exp son el momento en que creó la aserción en tiempo de epoch de Unix, por ejemplo, en .net haría algo como "WebConvert.EpocTime (DateTime.UtcNow)". Para "no antes" (nbf) puede restar un búfer para el sesgo del reloj, por ejemplo, 5 minutos; y para caduca en (exp) agregue algo de tiempo, por ejemplo, 15 minutos (para que la afirmación siga siendo válida para ese tiempo).

Aquí hay un rastro de violinista de una solicitud de token (sin procesar): POST https://login.windows.net/0e49ef1f-ca07-45f1-b4c0-ac9409d3e576/oauth2/token HTTP / 1.1 Content-Type: application / x-www- form-urlencoded client-request-id: a8108f88-275b-424d-ac28-f675aabe548e return-client-request-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 Longitud del contenido: 983 Espere: 100-continuar Conexión: Keep-Alive

recursos = https% 3A% 2F% 2Fgraph.windows.net% 2F y client_id = f17bb8a5-2bef-4ad5-a83f-cd7113449fc2 y client_assertion_type = urna% 3Aietf% 3Aparams% 3Aoauth% de tipo afirmación 3Aclient% 3Ajwt portador y client_assertion = eyJhbGciOiJSUzI1NiIsIng1dCI6ImY4S2JVY0xtMnItS2s4b1Z3ZVZYTFU0NzhJcyJ9.eyJhdWQiOiJodHRwczpcL1wvbG9naW4ud2luZG93cy5uZXRcLzBlNDllZjFmLWNhMDctNDVmMS1iNGMwLWFjOTQwOWQzZTU3Nlwvb2F1dGgyXC90b2tlbiIsImV4cCI6MTQyMjk4NDMzNSwiaXNzIjoiZjE3YmI4YTUtMmJlZi00YWQ1LWE4M2YtY2Q3MTEzNDQ5ZmMyIiwianRpIjoiZTI3OTA5YTctZGYwMC00NjBhLTlmZjctOGZkNDExOWVmNTYzIiwibmJmIjoxNDIyOTgzNzM1LCJzdWIiOiJmMTdiYjhhNS0yYmVmLTRhZDUtYTgzZi1jZDcxMTM0NDlmYzIifQ.g9bo4- lxpNJ4kEOMuQxODU-5iakwSVIzyRQEPLdbpuNn_XD4lcvt2yBIWT12EQaUVKkMyqFrDiIh4Oav565-Po7HfhmSPF3URXVj8Kx5lx17Zh0nWiaNkRXEi1vhwswsfjm1o-8B8LGUJTtT6JXTognrueuSL1aEE_-4qSG1y74aoc949Un1pQCjwuBtao4vs4CPJLu9Y9mVbirVRRtiIfxkUMmzf6yfMtuhugoGmrvUYntUo4x6N2fu4LxGjuIs7czyrMMAmDRo-XK4sAhDo5uof10HKb8ETEU8mhObwNZcz86MYHWbZm3Z_HDOwzC9kA_tp6hWqmlJ3c-gLg5VXA y grant_type = client_credentials

¡Espero que esto ayude! ¡Buena suerte!

Matías