In this sample, we show you how to integrate with school roles/roster data as well as O365 services available via the Graph API.
School data is kept in sync in O365 Education tenants by Microsoft School Data Sync.
Table of contents
- Sample Goals
- Prerequisites
- Register the application in Azure Active Directory
- Run the sample locally
- Deploy the sample to Azure
- Understand the code
- Questions and comments
- Contributing
The sample demonstrates:
-
Calling Graph APIs, including:
-
Linking locally-managed user accounts and Office 365 (Azure Active Directory) user accounts.
After linking accounts, users can use either local or Office 365 accounts to log into the sample website and use it.
-
Getting schools, sections, teachers, and students from Office 365 Education:
The sample is implemented with Ruby on Rail.
Deploying and running this sample requires:
-
An Azure subscription with permissions to register a new application, and deploy the web app.
-
An O365 Education tenant with Microsoft School Data Sync enabled
- One of the following browsers: Edge, Internet Explorer 9, Safari 5.0.6, Firefox 5, Chrome 13, or a later version of one of these browsers.
Additionally: Developing/running this sample locally requires the following:
- The Ruby language version 2.2.2 or newer.
- The RubyGems packaging system, which is installed with Ruby by default. To learn more about RubyGems, please read the RubyGems Guides.
- The Rails web application development framework, version 5.0.0 or above
- A working installation of the SQLite3 Database.
-
Sign into the new Azure portal: https://portal.azure.com/.
-
Choose your Azure AD tenant by selecting your account in the top right corner of the page:
-
Click Azure Active Directory -> App registrations -> +Add.
-
Input a Name, and select Web app / API as Application Type.
Input Sign-on URL: http://localhost:3000/
Click Create.
-
Once completed, the app will show in the list.
-
Click it to view its details.
-
Click All settings, if the setting window did not show.
-
Click Properties, then set Multi-tenanted to Yes.
Copy aside Application ID, then Click Save.
-
Click Required permissions. Add the following permissions:
API Application Permissions Delegated Permissions Microsoft Graph Read directory data
Access directory as the signed in user
Sign users in
Have full access to all files user can access
Have full access to user files
Read users' class assignments without grades
Read and write users' class assignments without grades
Read users' class assignments and their grades
Read and write users' class assignments and their gradesWindows Azure Active Directory Sign in and read user profile
Read and write directory data-
Application Permissions
Permission Description Read directory data Allows the app to read data in your organization's directory, such as users, groups and apps, without a signed-in user. Delegated Permissions
Permission Description Read all users' full profiles Allows the app to read the full set of profile properties, reports, and managers of other users in your organization, on behalf of the signed-in user. Read directory data Allows the app to read data in your organization's directory, such as users, groups and apps. Access directory as the signed in user Allows the app to have the same access to information in the directory as the signed-in user. Sign users in Allows users to sign in to the app with their work or school accounts and allows the app to see basic user profile information. Sign in and read user profile Allows users to sign-in to the app, and allows the app to read the profile of signed-in users. It also allows the app to read basic company information of signed-in users. Read and write directory data Allows the app to read and write data in your organization's directory, such as users, and groups. It does not allow the app to delete users or groups, or reset user passwords.
-
-
Click Keys, then add a new key:
Click Save, then copy aside the VALUE of the key.
Close the Settings window.
-
You need to have some prerequisites installed:
- The Ruby language version 2.3 or newer.
- The RubyGems packaging system, which is installed with Ruby by default. To learn more about RubyGems, please read the RubyGems Guides.
- The rails version 5.0.0 or above.
- A working installation of the SQLite3 Database.
Run the EDUGraphAPI:
-
Configure the following Environment Variables:
- ClientId: use the Client Id of the app registration you created earlier.
- ClientSecret: use the Key value of the app registration you created earlier.
- SourceCodeRepositoryURL: use the URL of this repository.
Or update these values in
config/settings.ymldirectly. -
Open terminal and navigate to the source code folder. Execute the command below:
bundle install rails db:schema:load rails db:seed rails s
-
Open http://localhost:3000 in a browser.
GitHub Authorization
-
Generate Token
- Open https://github.com/settings/tokens in your web browser.
- Sign into your GitHub account where you forked this repository.
- Click Generate Token
- Enter a value in the Token description text box
- Select the followings (your selections should match the screenshot below):
- repo (all) -> repo:status, repo_deployment, public_repo
- admin:repo_hook -> read:repo_hook
- Click Generate token
- Copy the token
-
Add the GitHub Token to Azure in the Azure Resource Explorer
- Open https://resources.azure.com/providers/Microsoft.Web/sourcecontrols/GitHub in your web browser.
- Log in with your Azure account.
- Selected the correct Azure subscription.
- Select Read/Write mode.
- Click Edit.
- Paste the token into the token parameter.
- Click PUT
Deploy the Azure Components from GitHub
-
Check to ensure that the build is passing VSTS Build.
-
Fork this repository to your GitHub account.
-
Click the Deploy to Azure Button:
-
Fill in the values in the deployment page:
Note: This ARM Template will create a Web App on Linux which is currently only available in the following regions:
- West US
- West Europe
- Southeast Asia
- Resource group: we suggest you create a new group.
- Location: please do choose one of the region above.
-
Site Name: please input a name. Like EDUGraphAPICanviz or EDUGraphAPI993.
Note: If the name you input is taken, you will get some validation errors:
Click it you will get more details like storage account is already in other resource group/subscription.
In this case, please use another name.
- Hosting Plan Name: the built-in Ruby 2.3 Docker image does not work well with the Kudu currently. Please use our customized image: tylerlu/ruby:2.3-0. Its source code is in the Docker-ruby2.3-0 folder.
- Source Code Repository URL: replace with the repository name of your fork.
- Source Code Manual Integration: choose false, since you are deploying from your own fork.
- Client Id: use the Client Id of the app registration you created earlier.
- Client Secret: use the Key value of the app registration you created earlier.
- Check I agree to the terms and conditions stated above.
-
Click Purchase.
Add REPLY URL to the app registration
-
After the deployment, open the resource group:
-
Click the web app.
Copy the URL aside, change the schema to https, and add a trailing slash. This is the replay URL and will be used in next step.
-
Navigate to the app registration in the new Azure portal, then open the setting windows.
Add the reply URL:
Note: to debug the sample locally, make sure that http://localhost:3000/ is in the reply URLs.
-
Click SAVE.
Solution Component Diagram
Authentication Mechanisms
OmniAuth OAuth2 and a custom Azure OAuth2 strategy lib/omniauth/azure_oauth2.rb are used to enable O365 users login.
Data Access
Active Record is used to access data from the database.
The models are in the app/models folders, and the database schema is in the db folder.
Below are the main tables used in this sample:
| Table | Description |
|---|---|
| users | Contains the user's information: name, email, hashed password... o365_user_id and o365_email are used to connect the local user with an O365 user. |
| user_roles | Contains users' role. Three roles are used in this sample: admin, teacher, and student. |
| organizations | A row in this table represents a tenant in AAD. IsAdminConsented column records than if the tenant consented by an administrator. |
| token_caches | Contains the users' access/refresh tokens. |
| classroom_seating_arrangements | Contains the classroom seating arrangements. |
You may change the database settings in the /config/database.yml file. SQLite is used for the development environment.
Libs
In the lib folder, there are 2 libs.
| Lib | Description |
|---|---|
| education | Contains EducationService and several model classes. They encapsulate education REST APIs. |
| omniauth | Contains AzureOauth2 class which implemented Azure OAuth2 strategy for OmniAuth |
Controllers
In the app/controllers folder, there are 6 controllers:
| Controller | Description |
|---|---|
| ApplicationController | The base controller of the other controllers. |
| AccountController | Contains actions for user to register, login and logout. |
| LinkController | Contains actions used for users to link accounts. |
| ManageController | Contains the about me action. |
| AdminController | Contains administrative actions like consent tenant, manage linked accounts. |
| SchoolsController | Contains actions used to show schools data. |
| ClassesController | Contains actions used to show classes data. |
Services
Service classes are in the app/services folder. Below are the main services used in the sample:
| Service | Description |
|---|---|
| MSGraphService | Contains methods used to access MS Graph APIs |
| AADGraphService | Contains methods used to access AAD Graph APIs |
| TokenCacheService | Contains method used to get and update cache from the database |
| UserService | Contains method used to manipulate users in the database |
| OrganizationService | Contains methods that operate organizations in the database |
| LinkService | Contains methods used to link user accounts |
Action filters
In the ApplicationController, several action filters were created and used by itself and other controllers.
| Name | Type | Description |
|---|---|---|
| require_login | before_action | Redirects user to login page if the user is not logged in. |
| admin_only | before_action | Only allow admin users to access the protected actions. |
| linked_users_only | before_action | Only allow linked users to access the protected actions. |
| handle_refresh_token_error | around_action | Rescue RefreshTokenError raised by TokenService when refresh token is missing or expired. It will redirect the user to page which explains the reason and instructs the user to re-login. |
Multi-tenant app
This web application is a multi-tenant app. In the AAD, we enabled the option:
Users from any Azure Active Directory tenant can access this app. Some permissions used by this app require an administrator of the tenant to consent before users can use the app. Otherwise, users will see this error:
For more information, see Build a multi-tenant SaaS web application using Azure AD & OpenID Connect.
The Office 365 Education APIs return data from any Office 365 tenant which has been synced to the cloud by Microsoft School Data Sync. The APIs provide information about schools, sections, teachers, students, and rosters. The Schools REST API provides access to school entities in Office 365 for Education tenants.
In this sample, the lib/education lib encapsulates the Office 365 Education API.
The EducationService is the core class of the library. It is used to easily get education data.
Get schools
def get_all_schools
get_objects(Education::School, 'education/schools')
enddef get_school(id)
get_object(Education::School, "education/schools/#{id}")
endGet classes
def get_classes(school_id, skip_token = nil, top = 12)
get_paged_objects(Education::Class, "education/schools/#{school_id}/classes", {
'$top': top,
'$skiptoken': skip_token,
'$expand': 'members'
})
enddef get_class(class_id)
get_object(Education::Class, "education/classes/#{class_id}")
endManage assignment
def create_assignment(class_id, assignment_obj)
request('post', "education/classes/#{class_id}/assignments", {}, assignment_obj.to_json)
end
def publish_assignment(class_id, assignment_id)
request('post', "education/classes/#{class_id}/assignments/#{assignment_id}/publish", {}, nil)
end
def add_assignment_resources(class_id, assignment_id,file_name, file_type,resource_url)
data = {"resource":{"displayName":file_name, "@odata.type":file_type, "file":{"odataid":"#{@base_url}/#{resource_url}"}} }
request('post', "education/classes/#{class_id}/assignments/#{assignment_id}/resources", {}, data.to_json)
end
def get_assignment_resources(class_id, assignment_id)
get_objects(Education::EducationAssignmentResource, "education/classes/#{class_id}/assignments/#{assignment_id}/resources")
end
Below are some screenshots of the sample app that show the education data.
There are 4 authentication flows in this project.
The first 2 flows (Local login_o365 Login) enable users to login in with either a local account or an Office 365 account, then link to the other type account. This procedure is implemented in the LinkController.
Local Login Authentication Flow
O365 Login Authentication Flow
Admin Login Authentication Flow
This flow shows how an administrator logs into the system and performs administrative operations.
After logging into the app with an Office 365 account, the administrator will be asked to link to a local account. This step is not required and can be skipped.
As mentioned earlier, the web app is a multi-tenant app which uses some application permissions, so tenant administrator must consent the app first.
This flow is implemented in the AdminController.
There are two distinct Graph APIs used in this sample:
| Azure AD Graph API | Microsoft Graph API | |
|---|---|---|
| Description | The Azure Active Directory Graph API provides programmatic access to Azure Active Directory through REST API endpoints. Apps can use the Azure AD Graph API to perform create, read, update, and delete (CRUD) operations on directory data and directory objects, such as users, groups, and organizational contacts | A unified API that also includes APIs from other Microsoft services like Outlook, OneDrive, OneNote, Planner, and Office Graph, all accessed through a single endpoint with a single access token. |
| Client | Install-Package Microsoft.Azure.ActiveDirectory.GraphClient | Install-Package Microsoft.Graph |
| End Point | https://graph.windows.net | https://graph.microsoft.com |
| API Explorer | https://graphexplorer.cloudapp.net/ | https://graph.microsoft.io/graph-explorer |
IMPORTANT NOTE: Microsoft is investing heavily in the new Microsoft Graph API, and they are not investing in the Azure AD Graph API anymore (except fixing security issues).
Therefore, please use the new Microsoft Graph API as much as possible and minimize how much you use the Azure AD Graph API.
Below is a piece of code shows how to get user photo from the Microsoft Graph API.
def get_user_photo(o365_user_id)
url = @base_url + "/users/#{o365_user_id}/photo/$value"
HTTParty.get(url, headers: {
"Authorization" => "Bearer #{@access_token}"
})
endNote that in the AAD Application settings, permissions for each Graph API are configured separately:
- If you have any trouble running this sample, please log an issue.
- Questions about GraphAPI development in general should be posted to Stack Overflow. Make sure that your questions or comments are tagged with [ms-graph-api].
We encourage you to contribute to our samples. For guidelines on how to proceed, see our contribution guide.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
Copyright (c) 2017 Microsoft. All rights reserved.

























