In an earlier post, I described my attempt to save some generated excel documents to OneDrive using OAUTH 2.0 client_credentials through the Graph API. That blog post is located here.
OneDrive is for individual use. In our organization, we also use SharePoint. SharePoint 365 also uses OneDrive for its backend file system. However, Microsoft has different APIs to save data into SharePoint sites through its Office 365 APIs. This blog post will explain how you can configure your Azure Portal and Office 365 to save documents to SharePoint programatically.
Overview of Technical Approach
This call returns the following response.
Following curl command tries to use this access token to access /drive in SharePoint 365.
This request comes back with the following error: Unsupported app only token
In order to eliminate this issue, we need to generate JWT access tokens using X.509 private/public certificates. To achieve it, I created a self-signed certificate with a key pair. I used this tool https://github.com/formio/keycred to achieve this generation. It basically generates the cert, the private key and the "Key Credentials" section that is required to be updated in our applications manifest file in Azure Management Portal.
This method uses Java's Keystore abstraction to access certificate and key details. It also uses ADAL4J library for authenticating against Office 365. In order to use this, we need to create this "keystore" and populate it with our certificate and private key. I used the following command to achieve this.
Once the above unit test runs, you will get an access token if everything is correct. You can then use this access token to call SharePoint APIs as seen below.
OneDrive is for individual use. In our organization, we also use SharePoint. SharePoint 365 also uses OneDrive for its backend file system. However, Microsoft has different APIs to save data into SharePoint sites through its Office 365 APIs. This blog post will explain how you can configure your Azure Portal and Office 365 to save documents to SharePoint programatically.
Overview of Technical Approach
Office 365/SharePoint provide OAUTH 2.0 enabled APIs for access. In order to use such APIs, applications need to be registered with our Azure Management Portal. Once applications are registered, OAUTH 2.0 with client certificates will be used to eliminate user consent flow as we will be using these APIs programmatically by back-end applications. There will be no end-user logging into SharePoint. Here is the documentation provided by OneDrive that talks about how to create and register your application in order to use these APIS.
Application Name: | Backend Application |
Single Sign On Url: | https://backend |
App Uri Id: | https://backenduri |
Application Client Id: | XXXXX |
Application Client Secret: | YYYYY |
Application Permissions
Once the application above was created and registered, it needed to be given permissions to other applications so it can access APIs. Following permissions were given in order to allow this application to access OneDrive APIs.
Application Name
|
Application Permission
|
Delegated Permisions
|
---|---|---|
Microsoft Graph |
Read and write files in all site collections
Read files in all site collections | Read user files |
Office 365 SharePoint Online | Read items in all site collections Read and write items in all site collections | Read and write user files Read user files |
Windows Azure Active Directory | None |
How to Generate a JWT Access Token
JWT access token generation for SharePoint 365 is slightly different than how we generated access tokens for OneDrive 365. Due to strict security requirements, access to SharePoint 365 APIs have to go through certificate based authentication. Though using the method identified in OneDrive 365 access get us an access token, its not accepted by the SharePoint APIs. Below is an example of CURL calls that shows the issue. Notice resource is for a SharePoint site.
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 0bac06f9-aba2-7857-4adc-d176829de817" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "grant_type=client_credentials" -F "client_id=YYYYYYY" -F "client_secret=XXXXXXXX" -F "redirect_uri=https://backenduri" -F "resource=https://azure_domain.sharepoint.com" "https://login.microsoftonline.com/azure_domain/oauth2/token"
This call returns the following response.
{
"token_type": "Bearer",
"expires_in": "3600",
"ext_expires_in": "0",
"expires_on": "1474919860",
"not_before": "1474915960",
"resource": "https://azure_domain.sharepoint.com",
"access_token": "xtyeyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiJodHRwczovL2lzbWFpbGNvLnNoYXJlcG9pbnQuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDZiNDhiZjUtMjYxMy00YjQwLWI4NGItZDQ2NzZhMWUzOWQxLyIsImlhdCI6MTQ3NDkxNTk2MCwibmJmIjoxNDc0OTE1OTYwLCJleHAiOjE0NzQ5MTk4NjAsImFwcF9kaXNwbGF5bmFtZSI6ImNyaXRpY2FsbW9uaXRvciIsImFwcGlkIjoiZGJiMDdmNWUtMWIyYS00NjNjLTgyYzgtYTJkY2Y2ZmM4NGI5IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDZiNDhiZjUtMjYxMy00YjQwLWI4NGItZDQ2NzZhMWUzOWQxLyIsIm9pZCI6ImU5ZTEwODE4LWY0ZTAtNGM0OC05MDBhLTIwMzNkMjRjYzlmZCIsInJvbGVzIjpbIlNpdGVzLlJlYWQuQWxsIiwiU2l0ZXMuUmVhZFdyaXRlLkFsbCJdLCJzdWIiOiJlOWUxMDgxOC1mNGUwLTRjNDgtOTAwYS0yMDMzZDI0Y2M5ZmQiLCJ0aWQiOiI0NmI0OGJmNS0yNjEzLTRiNDAtYjg0Yi1kNDY3NmExZTM5ZDEiLCJ2ZXIiOiIxLjAifQ.ZUPvXWGx7iyZXEBX-6lWMflKDiFHdsDC_VqHCnltaAD6KvmXZb-7xCwlUT4vpfgSAFAMK6Vy-4fldYl6GjC_Vcc5pvNiWtiSjy6tz7wjal2YhGxZPGqIptF0OU_OZe2fTgh7iwFHKIR2er2H1CYVtxmRylW5aaBPkGMkQ-u8FYfsiMjLUnR5ixgqLX25UOO4YBeDf5C2JJUb1ncINM2LDzKsqGYHN2alpMcaanhxSTZOpVVKZw2-5q5KNUwQveS7gQOncjAMjW4G0FrhfzIviYy_Y9jK-KzcH7pUIoRdJExWRfoWsu63PjSglCPGTgcjxSFpWl9b0FDmYYD9nfNlIw"
}
Following curl command tries to use this access token to access /drive in SharePoint 365.
curl -X GET -H "Authorization: Bearer xtyeyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiJodHRwczovL2lzbWFpbGNvLnNoYXJlcG9pbnQuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDZiNDhiZjUtMjYxMy00YjQwLWI4NGItZDQ2NzZhMWUzOWQxLyIsImlhdCI6MTQ3NDkxNTk2MCwibmJmIjoxNDc0OTE1OTYwLCJleHAiOjE0NzQ5MTk4NjAsImFwcF9kaXNwbGF5bmFtZSI6ImNyaXRpY2FsbW9uaXRvciIsImFwcGlkIjoiZGJiMDdmNWUtMWIyYS00NjNjLTgyYzgtYTJkY2Y2ZmM4NGI5IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDZiNDhiZjUtMjYxMy00YjQwLWI4NGItZDQ2NzZhMWUzOWQxLyIsIm9pZCI6ImU5ZTEwODE4LWY0ZTAtNGM0OC05MDBhLTIwMzNkMjRjYzlmZCIsInJvbGVzIjpbIlNpdGVzLlJlYWQuQWxsIiwiU2l0ZXMuUmVhZFdyaXRlLkFsbCJdLCJzdWIiOiJlOWUxMDgxOC1mNGUwLTRjNDgtOTAwYS0yMDMzZDI0Y2M5ZmQiLCJ0aWQiOiI0NmI0OGJmNS0yNjEzLTRiNDAtYjg0Yi1kNDY3NmExZTM5ZDEiLCJ2ZXIiOiIxLjAifQ.ZUPvXWGx7iyZXEBX-6lWMflKDiFHdsDC_VqHCnltaAD6KvmXZb-7xCwlUT4vpfgSAFAMK6Vy-4fldYl6GjC_Vcc5pvNiWtiSjy6tz7wjal2YhGxZPGqIptF0OU_OZe2fTgh7iwFHKIR2er2H1CYVtxmRylW5aaBPkGMkQ-u8FYfsiMjLUnR5ixgqLX25UOO4YBeDf5C2JJUb1ncINM2LDzKsqGYHN2alpMcaanhxSTZOpVVKZw2-5q5KNUwQveS7gQOncjAMjW4G0FrhfzIviYy_Y9jK-KzcH7pUIoRdJExWRfoWsu63PjSglCPGTgcjxSFpWl9b0FDmYYD9nfNlIw" -H "Cache-Control: no-cache" -H "Postman-Token: fbeaa4e7-575c-4bf8-b757-9f8bfdf552b3" "https://azure_domain/_api/v2.0/drive/"
This request comes back with the following error: Unsupported app only token
In order to eliminate this issue, we need to generate JWT access tokens using X.509 private/public certificates. To achieve it, I created a self-signed certificate with a key pair. I used this tool https://github.com/formio/keycred to achieve this generation. It basically generates the cert, the private key and the "Key Credentials" section that is required to be updated in our applications manifest file in Azure Management Portal.
Generate Certificate
Using keycred tool above, we can easily generate a public / private key pair with "Key Credentials" section auto generated for us as seen below.
prompt: Would you like to generate a new Certificate or use an existing one?
1.) Generate New
2.) Use Existing: 1
prompt: Country Name (2 letter code) [AU]: US
prompt: State or Province Name (full name) [Some-State]: Texas
prompt: Locality Name (eg, city) []: Dallas
prompt: Organization Name (eg, company) [Internet Widgits Pty Ltd]: Form.io
prompt: Organizational Unit Name (eg, section) []: IT
prompt: Common Name (e.g. server FQDN or YOUR name): Travis Tidwell
Generating key pairs
Creating a certificate.
Signing the certificate.
Key Credentials:
{
"customKeyIdentifier": "abrvf2N5nwK5Lkqfkku5xstfkWI=",
"value": "MIID1TCCAr2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBmMRcwFQYDVQQDEw5UcmF2aXMgVGlkd2VsbDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZEYWxsYXMxEDAOBgNVBAoTB0Zvcm0uaW8xCzAJBgNVBAsTAklUMB4XDTE1MDgwNzE2MzQwNFoXDTE2MDgwNzE2MzQwNFowZjEXMBUGA1UEAxMOVHJhdmlzIFRpZHdlbGwxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVUZXhhczEPMA0GA1UEBxMGRGFsbGFzMRAwDgYDVQQKEwdGb3JtLmlvMQswCQYDVQQLEwJJVDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3l+IzZ/3gTzNZHUTTDwJYeq0/yRaGofUJFG3mD5GBqlr5+17+SqLuWwHXfoV8tPckhuFYTL4HhKGm+GO0EHpL+i8bbweMhfRUDvhKBiUxZtZpalqeWKTkhB7tFs5lWepgzZ/JTsKwkCtCZp2D6UJ7+HbfqQ/jOBlFXITbtsOLiGFQI0WTgtHPg9Bj2kZin4UjNpOdvE0qK7XFCiPkyAUGvGHqetq2dw1Z9rxQ5w03ZZIL1DTVNtN3mxC8PX5Bcc0IOcsSNLqcXDt43+amTMYBMWbYYFOYjqZVEdIXf+ZzOJzjwXoFL53RYxKHdZXWC8y4f6dd0jzwNIvepnx7SDa8CAwEAAaOBjTCBijAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIC9DA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwEQYJYIZIAYb4QgEBBAQDAgD3MB0GA1UdDgQWBBSFghpkrNaarwWD5lyvy1ll95iU+DANBgkqhkiG9w0BAQUFAAOCAQEArl/lxpD9lRuRPiaWgIipECkxZv3bOyaS/x1I23H21w1t7w5aNac6PxvJhvA0qehNqT/nYGmopCYBDEv7nLGWIwZNUKAcIXGmNZWoOkH9d8RP45h3Mb0anWd8t/WuJr/Q3Jby5UvKeVKec2ews2wr8qm+AeONF1KwWUe7pyQN5G4bwbOTrFeO4Epf/Q9jxnN8xH7Jx5hSkVOQGOKa8vB80QKewKAeF3XcEqFTiLpvulmwiBiUAEVpjJ2FEw3+3QygSBkbzFdz6F8w644AL7ep0TpSXHDh07hEugsMx6xOCAD6e40pLCkT2y4UDAoeHovhD3jD7dUEuv5UyWiTu3esLw==",
"keyId": "f1f70508-8ec6-42b0-9bdc-5e0514e7cc4b",
"usage": "Verify",
"type": "AsymmetricX509Cert"
}
Private Key:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAveX4jNn/eBPM1kdRNMPAlh6rT/JFoah9QkUbeYPkYGqWvn7X
v5Kou5bAdd+hXy09ySG4VhMvgeEoab4Y7QQekv6LxtvB4yF9FQO+EoGJTFm1mlqW
p5YpOSEHu0WzmVZ6mDNn8lOwrCQK0JmnYPpQnv4dt+pD+M4GUVchNu2w4uIYVAjR
ZOC0c+D0GPaRmKfhSM2k528TSortcUKI+TIBQa8Yep62rZ3DVn2vFDnDTdlkgvUN
NU203ebELw9fkFxzQg5yxI0upxcO3jf5qZMxgExZthgU5iOplUR0hd/5nM4nOPBe
gUvndFjEod1ldYLzLh/p13SPPA0i96mfHtINrwIDAQABAoIBAAKQeXPFedqwQcLf
Aay5u/8K+PtCZUhSkyZy8nUIn+vhZJm78sPmghrOZVjyJSa44K23o9qdtbWGSdpr
s8B3nsYTY+XSj+K2UA8ZltZ/I1CBoZ8s+/3VXEcmOAmCXnOCpHMrtoqiMK+SaQb6
HDWGC9Kp0dLq+fIBLjqo94zbVtkpeH4rpUSoh1/fFCcAYgGD0CV57yZULqboGacS
sCBNCJnd9iJzD65GrY4kqKcABK+p0SOepkdQJMDx6dFJY/GAsske0kI+qb2pFt3t
wG5cDr4XVFTtjmb/sXaQ0iKGwfmLcVatlYlbuQg4R3OoPrNQgSmjtV/fpOlnP0yq
4JUSQzkCgYEA6BpLtMGk+k8/4BfP+f+tU2W8iRkPYKKChYwlH3FJNIjVu0SKX7Tf
vwgkAL/ar0aUFaXvWPYvz8aglq4Tz1V6uir5rkNH6/MgIhz7i0vlAlsXRR5obdYg
OmNb4X+tLzAzROE7p4pg9QoiGAgeOq8nAOIYuq0pp8Srym0+cbBXqrsCgYEA0XND
NET90JcMGXoyvTtPHiWTGNZgkjuhQ/nX7k6WrTiKrvRcCjf81hNHOiS85Bo3ev6j
YhsgMJBIj4HKUlusIB8BRvHRROxrorKYBjK6TcO0PCW/d3ZiwiOm/2X58x+KI/iP
Vl3SPxY8jfTz/80+A5x7LPjSob1WJoW9QbHr+50CgYEAu2XPzkezHm6yEgrn3t+y
X+16he733XwYih8XMgXTp1j7yFGUr6VoKeM2vmwo8u+3TKtHTrqm68lhi4dNc09+
4aUlJrAn97e7MA/agSNr6bUGWsYsZtJF/x6N5smhWMJAAtMfySwPEIl38ZNMZPFa
OnpSoRaC1XQTiEMf8ccBYVUCgYAYo/S+C3fIuFxmCug5NucB5u29OEs4KOe59YSf
toMnccVu+7RwR1HpZW81uei29Rad06MpAYPx8qd2qpBAzCQdy0f9Lqmt8Bphk50q
7YDZcTKc+Nvjk/veVw9ocHjNT2KTBMToJjV70oPhN3YVG/I1vo0HJ2awPHQMKCOx
vNrESQKBgCMUef+XW3PCWihNBbSZP1xmvhJHM9MAmPufb+MgNi7+0I69YL00/a6e
NVPYP957RrGjf6hiTEKmc2ipZh/hIVW9nPIpWhJ+SsxWHj/2RQbIWIGRRQ7F5pls
0ibMc7T5K7AGVT0q0ppBLheFQkeSnPbHJrX40xILEkzd/0RLvC8X
-----END RSA PRIVATE KEY-----
Certificate:
-----BEGIN CERTIFICATE-----
MIID1TCCAr2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBmMRcwFQYDVQQDEw5UcmF2
aXMgVGlkd2VsbDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQH
EwZEYWxsYXMxEDAOBgNVBAoTB0Zvcm0uaW8xCzAJBgNVBAsTAklUMB4XDTE1MDgw
NzE2MzQwNFoXDTE2MDgwNzE2MzQwNFowZjEXMBUGA1UEAxMOVHJhdmlzIFRpZHdl
bGwxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVUZXhhczEPMA0GA1UEBxMGRGFsbGFz
MRAwDgYDVQQKEwdGb3JtLmlvMQswCQYDVQQLEwJJVDCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAL3l+IzZ/3gTzNZHUTTDwJYeq0/yRaGofUJFG3mD5GBq
lr5+17+SqLuWwHXfoV8tPckhuFYTL4HhKGm+GO0EHpL+i8bbweMhfRUDvhKBiUxZ
tZpalqeWKTkhB7tFs5lWepgzZ/JTsKwkCtCZp2D6UJ7+HbfqQ/jOBlFXITbtsOLi
GFQI0WTgtHPg9Bj2kZin4UjNpOdvE0qK7XFCiPkyAUGvGHqetq2dw1Z9rxQ5w03Z
ZIL1DTVNtN3mxC8PX5Bcc0IOcsSNLqcXDt43+amTMYBMWbYYFOYjqZVEdIXf+ZzO
JzjwXoFL53RYxKHdZXWC8y4f6dd0jzwNIvepnx7SDa8CAwEAAaOBjTCBijAMBgNV
HRMEBTADAQH/MAsGA1UdDwQEAwIC9DA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYB
BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwEQYJYIZIAYb4QgEB
BAQDAgD3MB0GA1UdDgQWBBSFghpkrNaarwWD5lyvy1ll95iU+DANBgkqhkiG9w0B
AQUFAAOCAQEArl/lxpD9lRuRPiaWgIipECkxZv3bOyaS/x1I23H21w1t7w5aNac6
PxvJhvA0qehNqT/nYGmopCYBDEv7nLGWIwZNUKAcIXGmNZWoOkH9d8RP45h3Mb0a
nWd8t/WuJr/Q3Jby5UvKeVKec2ews2wr8qm+AeONF1KwWUe7pyQN5G4bwbOTrFeO
4Epf/Q9jxnN8xH7Jx5hSkVOQGOKa8vB80QKewKAeF3XcEqFTiLpvulmwiBiUAEVp
jJ2FEw3+3QygSBkbzFdz6F8w644AL7ep0TpSXHDh07hEugsMx6xOCAD6e40pLCkT
2y4UDAoeHovhD3jD7dUEuv5UyWiTu3esLw==
-----END CERTIFICATE-----
Updating Azure Application Manifest file with Key Credentials
This is needed to make sure certificate authentication is allowed and works with this application. There is no UI in Azure Portal to achieve this by just modifying data. In order to configure this application with cert based authentication, we need to download this applications Manifest file. You can do this by logging into Azure Portal and going to Configuration details and downloading the manifest file. Once the manifest file is downloaded, open it with a text editor and modify its Key Credentials section.
Originally keyCredentials will be an empty array. Use the "Key Credentials" section generated above to edit this file. You will then need to upload this modified version of the manifest file using the "Upload Manifest" link as seen above. If everything goes as expected, Azure Portal will validate your update. If you run into issues, you will have to repeat this process. (Following snippet it was borrowed from the above github URL.)
...
...
"keyCredentials": [
{
"customKeyIdentifier": "abrvf2N5nwK5Lkqfkku5xstfkWI=",
"value": "MIID1TCCAr2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBmMRcwFQYDVQQDEw5UcmF2aXMgVGlkd2VsbDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZEYWxsYXMxEDAOBgNVBAoTB0Zvcm0uaW8xCzAJBgNVBAsTAklUMB4XDTE1MDgwNzE2MzQwNFoXDTE2MDgwNzE2MzQwNFowZjEXMBUGA1UEAxMOVHJhdmlzIFRpZHdlbGwxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVUZXhhczEPMA0GA1UEBxMGRGFsbGFzMRAwDgYDVQQKEwdGb3JtLmlvMQswCQYDVQQLEwJJVDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3l+IzZ/3gTzNZHUTTDwJYeq0/yRaGofUJFG3mD5GBqlr5+17+SqLuWwHXfoV8tPckhuFYTL4HhKGm+GO0EHpL+i8bbweMhfRUDvhKBiUxZtZpalqeWKTkhB7tFs5lWepgzZ/JTsKwkCtCZp2D6UJ7+HbfqQ/jOBlFXITbtsOLiGFQI0WTgtHPg9Bj2kZin4UjNpOdvE0qK7XFCiPkyAUGvGHqetq2dw1Z9rxQ5w03ZZIL1DTVNtN3mxC8PX5Bcc0IOcsSNLqcXDt43+amTMYBMWbYYFOYjqZVEdIXf+ZzOJzjwXoFL53RYxKHdZXWC8y4f6dd0jzwNIvepnx7SDa8CAwEAAaOBjTCBijAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIC9DA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwEQYJYIZIAYb4QgEBBAQDAgD3MB0GA1UdDgQWBBSFghpkrNaarwWD5lyvy1ll95iU+DANBgkqhkiG9w0BAQUFAAOCAQEArl/lxpD9lRuRPiaWgIipECkxZv3bOyaS/x1I23H21w1t7w5aNac6PxvJhvA0qehNqT/nYGmopCYBDEv7nLGWIwZNUKAcIXGmNZWoOkH9d8RP45h3Mb0anWd8t/WuJr/Q3Jby5UvKeVKec2ews2wr8qm+AeONF1KwWUe7pyQN5G4bwbOTrFeO4Epf/Q9jxnN8xH7Jx5hSkVOQGOKa8vB80QKewKAeF3XcEqFTiLpvulmwiBiUAEVpjJ2FEw3+3QygSBkbzFdz6F8w644AL7ep0TpSXHDh07hEugsMx6xOCAD6e40pLCkT2y4UDAoeHovhD3jD7dUEuv5UyWiTu3esLw==",
"keyId": "f1f70508-8ec6-42b0-9bdc-5e0514e7cc4b",
"usage": "Verify",
"type": "AsymmetricX509Cert"
}
],
...
...
Generate Access Token using Certs
Now that certificate authentication has been set up with our application, we will need use it to get an access token with it. In order to achieve this, I wrote a simple Java method as seen below.
@Test
public void testSharePointAccess() throws InterruptedException, ExecutionException, KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException,
UnrecoverableKeyException, NoSuchProviderException {
final String loginEndpoint = "https://login.microsoftonline.com/";
final String tenantId = "azure_domain";
final String authority = loginEndpoint + tenantId;
final String clientId = "XXXXX";
final String fileName = "keystore_name.p12";
final String password = "keypassword";
final String resourceId = "https://azure_domain.sharepoint.com";
ExecutorService service = Executors.newSingleThreadExecutor();
service = Executors.newFixedThreadPool(1);
final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE");
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource(fileName).getFile());
keystore.load(new FileInputStream(file), password.toCharArray());
final String alias = keystore.aliases().nextElement();
final PrivateKey key = (PrivateKey) keystore.getKey(alias, password.toCharArray());
final X509Certificate cert = (X509Certificate) keystore.getCertificate(alias);
final AsymmetricKeyCredential asymmetricKeyCredential = AsymmetricKeyCredential.create(clientId, key, cert);
AuthenticationContext ctx = new AuthenticationContext(authority, false, service);
final Future result = ctx.acquireToken(resourceId, asymmetricKeyCredential, null);
AuthenticationResult ar = result.get();
assertNotNull(ar.getAccessToken());
System.out.println(ar.getAccessToken());
}
openssl pkcs12 -export -in [path to certificate] -inkey [path to private key] -certfile [path to certificate ] -out keystore_name.p12
Once the above unit test runs, you will get an access token if everything is correct. You can then use this access token to call SharePoint APIs as seen below.
curl -X GET -H "Authorization: Bearer xtyeyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiJodHRwczovL2lzbWFpbGNvLnNoYXJlcG9pbnQuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDZiNDhiZjUtMjYxMy00YjQwLWI4NGItZDQ2NzZhMWUzOWQxLyIsImlhdCI6MTQ3NDk4OTA0NCwibmJmIjoxNDc0OTg5MDQ0LCJleHAiOjE0NzQ5OTI5NDQsImFwcF9kaXNwbGF5bmFtZSI6ImNyaXRpY2FsbW9uaXRvciIsImFwcGlkIjoiZGJiMDdmNWUtMWIyYS00NjNjLTgyYzgtYTJkY2Y2ZmM4NGI5IiwiYXBwaWRhY3IiOiIyIiwiZV9leHAiOjEwODAwLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC80NmI0OGJmNS0yNjEzLTRiNDAtYjg0Yi1kNDY3NmExZTM5ZDEvIiwib2lkIjoiZTllMTA4MTgtZjRlMC00YzQ4LTkwMGEtMjAzM2QyNGNjOWZkIiwicm9sZXMiOlsiU2l0ZXMuUmVhZC5BbGwiLCJTaXRlcy5SZWFkV3JpdGUuQWxsIl0sInN1YiI6ImU5ZTEwODE4LWY0ZTAtNGM0OC05MDBhLTIwMzNkMjRjYzlmZCIsInRpZCI6IjQ2YjQ4YmY1LTI2MTMtNGI0MC1iODRiLWQ0Njc2YTFlMzlkMSIsInZlciI6IjEuMCJ9.Sru6PybxB8VlbxxNMyI6N-35MEy-FQOOSy9nbt3kjZ4TzjpbhXpVlzmhoWTr3uub20O2abIYSBuibkmtvSYnL-wAOZCqGaRObJH9o6C7j573xP4iTKS9pLMOjcHh5VQOu9Zk9YvVSFyE-5m-cQ7GRuMM4NT5wNJ5hP1pR5xolE3f0ogg3A49DW_t4ioM-YZ-zmoljbrRx2iHoDLAKL45HGjTWZTj20biWB7a2hp-xZAGkKbFNEPLYBZLixLE7rgVQ6fM_IudRscwbRrzO1gC5W2pFn0e427izoQvswNGXmYrg2hIpUItCxxxX1DxmDpD-zAErJxZeW_H6QbZoONZpg" -H "Cache-Control: no-cache" -H "Postman-Token: dbf34db1-a398-c750-27f9-7ba705ee7371" "https://azure_domain.sharepoint.com/_api/v2.0/drives"
{
"@odata.context": "https://azure_domain.sharepoint.com/_api/v2.0/$metadata#drive",
"value": [
{
"@odata.type": "#oneDrive.drive",
"@odata.id": "https://azure_domain.sharepoint.com/_api/v2.0/drives/driveID1",
"@odata.editLink": "drives/driveID1",
"createdDateTime": "0001-01-01T00:00:00-08:00",
"id": "driveID1",
"lastModifiedDateTime": "0001-01-01T00:00:00-08:00",
"name": "Documents",
"driveType": "documentLibrary",
"owner": {
"user": {}
}
}
]
}
SharePoint Drive Access
By default accessing SharePoint Drive API gets us access to our "Team Site". In order to use a specific site, you will need to specify that before api resources.
- "https://azure_domin.sharepoint.com/_api/v2.0/drives" - this resource gets us access to "Team Site"
- "https://azure_domain.sharepoint.com/test/_api/v2.0/drives" - this resource get us access to a specific site named "test".
Comments