├── src
└── main
│ ├── webapp
│ ├── index.html
│ └── WEB-INF
│ │ ├── jsp
│ │ ├── index.jsp
│ │ ├── events.jsp
│ │ ├── contacts.jsp
│ │ └── mail.jsp
│ │ ├── web.xml
│ │ ├── dispatcher-servlet.xml
│ │ ├── defs
│ │ └── pages.xml
│ │ └── layout
│ │ └── base.jsp
│ ├── resources
│ └── auth.properties
│ └── java
│ └── com
│ └── outlook
│ └── dev
│ ├── service
│ ├── Recipient.java
│ ├── EmailAddress.java
│ ├── DateTimeTimeZone.java
│ ├── PagedResult.java
│ ├── OutlookUser.java
│ ├── OutlookService.java
│ ├── Event.java
│ ├── Contact.java
│ ├── Message.java
│ └── OutlookServiceBuilder.java
│ ├── auth
│ ├── TokenService.java
│ ├── TokenResponse.java
│ ├── IdToken.java
│ └── AuthHelper.java
│ └── controller
│ ├── IndexController.java
│ ├── MailController.java
│ ├── EventsController.java
│ ├── ContactsController.java
│ └── AuthorizeController.java
├── .settings
├── org.eclipse.wst.jsdt.ui.superType.name
├── org.eclipse.wst.validation.prefs
├── org.eclipse.wst.jsdt.ui.superType.container
├── org.eclipse.m2e.core.prefs
├── org.eclipse.wst.common.project.facet.core.xml
├── org.eclipse.jdt.core.prefs
├── .jsdtscope
└── org.eclipse.wst.common.component
├── readme-images
├── auth-code.PNG
├── hello-world.PNG
├── inbox-list.PNG
├── new-password.PNG
├── installed-jre.PNG
├── jetty-default.PNG
├── project-facets.PNG
├── app-registration.PNG
├── java-build-path.PNG
├── new-maven-project.PNG
└── add-context-namespace.PNG
├── .gitattributes
├── .springBeans
├── .gitignore
├── .project
├── .classpath
├── README.md
└── pom.xml
/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.jsdt.ui.superType.name:
--------------------------------------------------------------------------------
1 | Window
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.validation.prefs:
--------------------------------------------------------------------------------
1 | disabled=06target
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.jsdt.ui.superType.container:
--------------------------------------------------------------------------------
1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary
--------------------------------------------------------------------------------
/readme-images/auth-code.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/auth-code.PNG
--------------------------------------------------------------------------------
/readme-images/hello-world.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/hello-world.PNG
--------------------------------------------------------------------------------
/readme-images/inbox-list.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/inbox-list.PNG
--------------------------------------------------------------------------------
/readme-images/new-password.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/new-password.PNG
--------------------------------------------------------------------------------
/readme-images/installed-jre.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/installed-jre.PNG
--------------------------------------------------------------------------------
/readme-images/jetty-default.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/jetty-default.PNG
--------------------------------------------------------------------------------
/readme-images/project-facets.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/project-facets.PNG
--------------------------------------------------------------------------------
/readme-images/app-registration.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/app-registration.PNG
--------------------------------------------------------------------------------
/readme-images/java-build-path.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/java-build-path.PNG
--------------------------------------------------------------------------------
/readme-images/new-maven-project.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/new-maven-project.PNG
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/readme-images/add-context-namespace.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasonjoh/java-tutorial/HEAD/readme-images/add-context-namespace.PNG
--------------------------------------------------------------------------------
/src/main/resources/auth.properties:
--------------------------------------------------------------------------------
1 | appId=YOUR_APP_ID_HERE
2 | appPassword=YOUR_APP_PASSWORD_HERE
3 | redirectUrl=http://localhost:8080/authorize.html
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.common.project.facet.core.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.compliance=1.8
5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
7 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
8 | org.eclipse.jdt.core.compiler.source=1.8
9 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/Recipient.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class Recipient {
7 | private EmailAddress emailAddress;
8 |
9 | public EmailAddress getEmailAddress() {
10 | return emailAddress;
11 | }
12 |
13 | public void setEmailAddress(EmailAddress emailAddress) {
14 | this.emailAddress = emailAddress;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.springBeans:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 | src/main/webapp/WEB-INF/dispatcher-servlet.xml
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/EmailAddress.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class EmailAddress {
7 | private String name;
8 | private String address;
9 |
10 | public String getName() {
11 | return name;
12 | }
13 | public void setName(String name) {
14 | this.name = name;
15 | }
16 | public String getAddress() {
17 | return address;
18 | }
19 | public void setAddress(String address) {
20 | this.address = address;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/jsp/index.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2 | <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
3 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4 |
5 |
6 | ${error}
7 |
8 |
9 |
Java Web App Tutorial
10 |
This sample uses the Mail API to read messages in your inbox.
11 |
">Click here to login
12 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/DateTimeTimeZone.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import java.util.Date;
4 |
5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
6 |
7 | @JsonIgnoreProperties(ignoreUnknown = true)
8 | public class DateTimeTimeZone {
9 | private Date dateTime;
10 | private String timeZone;
11 |
12 | public Date getDateTime() {
13 | return dateTime;
14 | }
15 | public void setDateTime(Date dateTime) {
16 | this.dateTime = dateTime;
17 | }
18 | public String getTimeZone() {
19 | return timeZone;
20 | }
21 | public void setTimeZone(String timeZone) {
22 | this.timeZone = timeZone;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.settings/.jsdtscope:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/PagedResult.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | @JsonIgnoreProperties(ignoreUnknown = true)
7 | public class PagedResult {
8 | @JsonProperty("@odata.nextLink")
9 | private String nextPageLink;
10 | private T[] value;
11 |
12 | public String getNextPageLink() {
13 | return nextPageLink;
14 | }
15 | public void setNextPageLink(String nextPageLink) {
16 | this.nextPageLink = nextPageLink;
17 | }
18 | public T[] getValue() {
19 | return value;
20 | }
21 | public void setValue(T[] value) {
22 | this.value = value;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.common.component:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/OutlookUser.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class OutlookUser {
7 | private String id;
8 | private String mail;
9 | private String displayName;
10 |
11 | public String getId() {
12 | return id;
13 | }
14 | public void setId(String id) {
15 | this.id = id;
16 | }
17 | public String getMail() {
18 | return mail;
19 | }
20 | public void setMail(String emailAddress) {
21 | this.mail = emailAddress;
22 | }
23 | public String getDisplayName() {
24 | return displayName;
25 | }
26 | public void setDisplayName(String displayName) {
27 | this.displayName = displayName;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/jsp/events.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 |
4 |
5 | Error: ${error}
6 |
7 |
8 |
9 | Calendar
10 |
11 |
12 | | Organizer |
13 | Subject |
14 | Start |
15 | End |
16 |
17 |
18 |
19 |
20 |
21 | |
22 | |
23 | |
24 | |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
49 | # Java/Maven/STS
50 | target
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/jsp/contacts.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 |
4 |
5 | Error: ${error}
6 |
7 |
8 |
9 | Contacts
10 |
11 |
12 | | Name |
13 | Company |
14 | Email |
15 |
16 |
17 |
18 |
19 |
20 | |
21 | |
22 |
23 |
28 | |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/OutlookService.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import retrofit2.Call;
4 | import retrofit2.http.GET;
5 | import retrofit2.http.Path;
6 | import retrofit2.http.Query;
7 |
8 | public interface OutlookService {
9 |
10 | @GET("/v1.0/me")
11 | Call getCurrentUser();
12 |
13 | @GET("/v1.0/me/mailfolders/{folderid}/messages")
14 | Call> getMessages(
15 | @Path("folderid") String folderId,
16 | @Query("$orderby") String orderBy,
17 | @Query("$select") String select,
18 | @Query("$top") Integer maxResults
19 | );
20 |
21 | @GET("/v1.0/me/events")
22 | Call> getEvents(
23 | @Query("$orderby") String orderBy,
24 | @Query("$select") String select,
25 | @Query("$top") Integer maxResults
26 | );
27 |
28 | @GET("/v1.0/me/contacts")
29 | Call> getContacts(
30 | @Query("$orderby") String orderBy,
31 | @Query("$select") String select,
32 | @Query("$top") Integer maxResults
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/auth/TokenService.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.auth;
2 |
3 | import retrofit2.Call;
4 | import retrofit2.http.Field;
5 | import retrofit2.http.FormUrlEncoded;
6 | import retrofit2.http.POST;
7 | import retrofit2.http.Path;
8 |
9 | public interface TokenService {
10 |
11 | @FormUrlEncoded
12 | @POST("/{tenantid}/oauth2/v2.0/token")
13 | Call getAccessTokenFromAuthCode(
14 | @Path("tenantid") String tenantId,
15 | @Field("client_id") String clientId,
16 | @Field("client_secret") String clientSecret,
17 | @Field("grant_type") String grantType,
18 | @Field("code") String code,
19 | @Field("redirect_uri") String redirectUrl
20 | );
21 |
22 | @FormUrlEncoded
23 | @POST("/{tenantid}/oauth2/v2.0/token")
24 | Call getAccessTokenFromRefreshToken(
25 | @Path("tenantid") String tenantId,
26 | @Field("client_id") String clientId,
27 | @Field("client_secret") String clientSecret,
28 | @Field("grant_type") String grantType,
29 | @Field("refresh_token") String code,
30 | @Field("redirect_uri") String redirectUrl
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/Event.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class Event {
7 | private String id;
8 | private String subject;
9 | private Recipient organizer;
10 | private DateTimeTimeZone start;
11 | private DateTimeTimeZone end;
12 |
13 | public String getId() {
14 | return id;
15 | }
16 | public void setId(String id) {
17 | this.id = id;
18 | }
19 | public String getSubject() {
20 | return subject;
21 | }
22 | public void setSubject(String subject) {
23 | this.subject = subject;
24 | }
25 | public Recipient getOrganizer() {
26 | return organizer;
27 | }
28 | public void setOrganizer(Recipient organizer) {
29 | this.organizer = organizer;
30 | }
31 | public DateTimeTimeZone getStart() {
32 | return start;
33 | }
34 | public void setStart(DateTimeTimeZone start) {
35 | this.start = start;
36 | }
37 | public DateTimeTimeZone getEnd() {
38 | return end;
39 | }
40 | public void setEnd(DateTimeTimeZone end) {
41 | this.end = end;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/jsp/mail.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3 |
4 |
5 | Error: ${error}
6 |
7 |
8 |
9 | Inbox
10 |
11 |
12 | |
13 | From |
14 | Subject |
15 | Received |
16 | Preview |
17 |
18 |
19 |
20 |
21 |
22 | |
23 |
24 |
25 |
26 | |
27 | |
28 | |
29 | |
30 | |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/controller/IndexController.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.controller;
2 |
3 | import java.util.UUID;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpSession;
7 |
8 | import org.springframework.stereotype.Controller;
9 | import org.springframework.ui.Model;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 |
12 | import com.outlook.dev.auth.AuthHelper;
13 |
14 | @Controller
15 | public class IndexController {
16 |
17 | @RequestMapping("/index")
18 | public String index(Model model, HttpServletRequest request) {
19 | UUID state = UUID.randomUUID();
20 | UUID nonce = UUID.randomUUID();
21 |
22 | // Save the state and nonce in the session so we can
23 | // verify after the auth process redirects back
24 | HttpSession session = request.getSession();
25 | session.setAttribute("expected_state", state);
26 | session.setAttribute("expected_nonce", nonce);
27 |
28 | String loginUrl = AuthHelper.getLoginUrl(state, nonce);
29 | model.addAttribute("loginUrl", loginUrl);
30 | // Name of a definition in WEB-INF/defs/pages.xml
31 | return "index";
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | java-tutorial
4 |
5 | index.html
6 | index.htm
7 | index.jsp
8 | default.html
9 | default.htm
10 | default.jsp
11 |
12 |
13 |
14 | dispatcher
15 | org.springframework.web.servlet.DispatcherServlet
16 | 1
17 |
18 |
19 |
20 | dispatcher
21 | *.html
22 | *.htm
23 | *.json
24 | *.xml
25 |
26 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/Contact.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class Contact {
7 | private String id;
8 | private String givenName;
9 | private String surname;
10 | private String companyName;
11 | private EmailAddress[] emailAddresses;
12 |
13 | public String getId() {
14 | return id;
15 | }
16 | public void setId(String id) {
17 | this.id = id;
18 | }
19 | public String getGivenName() {
20 | return givenName;
21 | }
22 | public void setGivenName(String givenName) {
23 | this.givenName = givenName;
24 | }
25 | public String getSurname() {
26 | return surname;
27 | }
28 | public void setSurname(String surname) {
29 | this.surname = surname;
30 | }
31 | public String getCompanyName() {
32 | return companyName;
33 | }
34 | public void setCompanyName(String companyName) {
35 | this.companyName = companyName;
36 | }
37 | public EmailAddress[] getEmailAddresses() {
38 | return emailAddresses;
39 | }
40 | public void setEmailAddresses(EmailAddress[] emailAddresses) {
41 | this.emailAddresses = emailAddresses;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/dispatcher-servlet.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
16 |
17 |
18 | /WEB-INF/defs/pages.xml
19 |
20 |
21 |
22 |
23 |
25 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/Message.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import java.util.Date;
4 |
5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
6 |
7 | @JsonIgnoreProperties(ignoreUnknown = true)
8 | public class Message {
9 | private String id;
10 | private Date receivedDateTime;
11 | private Recipient from;
12 | private Boolean isRead;
13 | private String subject;
14 | private String bodyPreview;
15 |
16 | public String getId() {
17 | return id;
18 | }
19 | public void setId(String id) {
20 | this.id = id;
21 | }
22 | public Date getReceivedDateTime() {
23 | return receivedDateTime;
24 | }
25 | public void setReceivedDateTime(Date receivedDateTime) {
26 | this.receivedDateTime = receivedDateTime;
27 | }
28 | public Recipient getFrom() {
29 | return from;
30 | }
31 | public void setFrom(Recipient from) {
32 | this.from = from;
33 | }
34 | public Boolean getIsRead() {
35 | return isRead;
36 | }
37 | public void setIsRead(Boolean isRead) {
38 | this.isRead = isRead;
39 | }
40 | public String getSubject() {
41 | return subject;
42 | }
43 | public void setSubject(String subject) {
44 | this.subject = subject;
45 | }
46 | public String getBodyPreview() {
47 | return bodyPreview;
48 | }
49 | public void setBodyPreview(String bodyPreview) {
50 | this.bodyPreview = bodyPreview;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/defs/pages.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | java-tutorial
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.wst.common.project.facet.core.builder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 | org.springframework.ide.eclipse.core.springbuilder
25 |
26 |
27 |
28 |
29 | org.eclipse.m2e.core.maven2Builder
30 |
31 |
32 |
33 |
34 |
35 | org.springframework.ide.eclipse.core.springnature
36 | org.eclipse.jem.workbench.JavaEMFNature
37 | org.eclipse.wst.common.modulecore.ModuleCoreNature
38 | org.eclipse.jdt.core.javanature
39 | org.eclipse.m2e.core.maven2Nature
40 | org.eclipse.wst.common.project.facet.core.nature
41 | org.eclipse.wst.jsdt.core.jsNature
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/service/OutlookServiceBuilder.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.service;
2 |
3 | import java.io.IOException;
4 | import java.util.UUID;
5 |
6 | import okhttp3.Interceptor;
7 | import okhttp3.OkHttpClient;
8 | import okhttp3.Request;
9 | import okhttp3.Request.Builder;
10 | import okhttp3.Response;
11 | import okhttp3.logging.HttpLoggingInterceptor;
12 | import retrofit2.Retrofit;
13 | import retrofit2.converter.jackson.JacksonConverterFactory;
14 |
15 | public class OutlookServiceBuilder {
16 |
17 | public static OutlookService getOutlookService(String accessToken, String userEmail) {
18 | // Create a request interceptor to add headers that belong on
19 | // every request
20 | Interceptor requestInterceptor = new Interceptor() {
21 | @Override
22 | public Response intercept(Interceptor.Chain chain) throws IOException {
23 | Request original = chain.request();
24 | Builder builder = original.newBuilder()
25 | .header("User-Agent", "java-tutorial")
26 | .header("client-request-id", UUID.randomUUID().toString())
27 | .header("return-client-request-id", "true")
28 | .header("Authorization", String.format("Bearer %s", accessToken))
29 | .method(original.method(), original.body());
30 |
31 | Request request = builder.build();
32 | return chain.proceed(request);
33 | }
34 | };
35 |
36 | // Create a logging interceptor to log request and responses
37 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
38 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
39 |
40 | OkHttpClient client = new OkHttpClient.Builder()
41 | .addInterceptor(requestInterceptor)
42 | .addInterceptor(loggingInterceptor)
43 | .build();
44 |
45 | // Create and configure the Retrofit object
46 | Retrofit retrofit = new Retrofit.Builder()
47 | .baseUrl("https://graph.microsoft.com")
48 | .client(client)
49 | .addConverterFactory(JacksonConverterFactory.create())
50 | .build();
51 |
52 | // Generate the token service
53 | return retrofit.create(OutlookService.class);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/controller/MailController.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.controller;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpSession;
7 |
8 | import org.springframework.stereotype.Controller;
9 | import org.springframework.ui.Model;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.servlet.mvc.support.RedirectAttributes;
12 |
13 | import com.outlook.dev.auth.AuthHelper;
14 | import com.outlook.dev.auth.TokenResponse;
15 | import com.outlook.dev.service.Message;
16 | import com.outlook.dev.service.OutlookService;
17 | import com.outlook.dev.service.OutlookServiceBuilder;
18 | import com.outlook.dev.service.PagedResult;
19 |
20 | @Controller
21 | public class MailController {
22 |
23 | @RequestMapping("/mail")
24 | public String mail(Model model, HttpServletRequest request, RedirectAttributes redirectAttributes) {
25 | HttpSession session = request.getSession();
26 | TokenResponse tokens = (TokenResponse)session.getAttribute("tokens");
27 | if (tokens == null) {
28 | // No tokens in session, user needs to sign in
29 | redirectAttributes.addFlashAttribute("error", "Please sign in to continue.");
30 | return "redirect:/index.html";
31 | }
32 |
33 | String tenantId = (String)session.getAttribute("userTenantId");
34 |
35 | tokens = AuthHelper.ensureTokens(tokens, tenantId);
36 |
37 | String email = (String)session.getAttribute("userEmail");
38 |
39 | OutlookService outlookService = OutlookServiceBuilder.getOutlookService(tokens.getAccessToken(), email);
40 |
41 | // Retrieve messages from the inbox
42 | String folder = "inbox";
43 | // Sort by time received in descending order
44 | String sort = "receivedDateTime DESC";
45 | // Only return the properties we care about
46 | String properties = "receivedDateTime,from,isRead,subject,bodyPreview";
47 | // Return at most 10 messages
48 | Integer maxResults = 10;
49 |
50 | try {
51 | PagedResult messages = outlookService.getMessages(
52 | folder, sort, properties, maxResults)
53 | .execute().body();
54 | model.addAttribute("messages", messages.getValue());
55 | } catch (IOException e) {
56 | redirectAttributes.addFlashAttribute("error", e.getMessage());
57 | return "redirect:/index.html";
58 | }
59 |
60 | return "mail";
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/controller/EventsController.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.controller;
2 |
3 | import java.io.IOException;
4 | import java.util.Date;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpSession;
8 |
9 | import org.springframework.stereotype.Controller;
10 | import org.springframework.ui.Model;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.servlet.mvc.support.RedirectAttributes;
13 |
14 | import com.outlook.dev.auth.TokenResponse;
15 | import com.outlook.dev.service.Event;
16 | import com.outlook.dev.service.OutlookService;
17 | import com.outlook.dev.service.OutlookServiceBuilder;
18 | import com.outlook.dev.service.PagedResult;
19 |
20 | @Controller
21 | public class EventsController {
22 |
23 | @RequestMapping("/events")
24 | public String events(Model model, HttpServletRequest request, RedirectAttributes redirectAttributes) {
25 | HttpSession session = request.getSession();
26 | TokenResponse tokens = (TokenResponse)session.getAttribute("tokens");
27 | if (tokens == null) {
28 | // No tokens in session, user needs to sign in
29 | redirectAttributes.addFlashAttribute("error", "Please sign in to continue.");
30 | return "redirect:/index.html";
31 | }
32 |
33 | Date now = new Date();
34 | if (now.after(tokens.getExpirationTime())) {
35 | // Token expired
36 | // TODO: Use the refresh token to request a new token from the token endpoint
37 | // For now, just complain
38 | redirectAttributes.addFlashAttribute("error", "The access token has expired. Please logout and re-login.");
39 | return "redirect:/index.html";
40 | }
41 |
42 | String email = (String)session.getAttribute("userEmail");
43 |
44 | OutlookService outlookService = OutlookServiceBuilder.getOutlookService(tokens.getAccessToken(), email);
45 |
46 | // Sort by start time in descending order
47 | String sort = "start/DateTime DESC";
48 | // Only return the properties we care about
49 | String properties = "organizer,subject,start,end";
50 | // Return at most 10 events
51 | Integer maxResults = 10;
52 |
53 | try {
54 | PagedResult events = outlookService.getEvents(
55 | sort, properties, maxResults)
56 | .execute().body();
57 | model.addAttribute("events", events.getValue());
58 | } catch (IOException e) {
59 | redirectAttributes.addFlashAttribute("error", e.getMessage());
60 | return "redirect:/index.html";
61 | }
62 |
63 | return "events";
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/controller/ContactsController.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.controller;
2 |
3 | import java.io.IOException;
4 | import java.util.Date;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpSession;
8 |
9 | import org.springframework.stereotype.Controller;
10 | import org.springframework.ui.Model;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.servlet.mvc.support.RedirectAttributes;
13 |
14 | import com.outlook.dev.auth.TokenResponse;
15 | import com.outlook.dev.service.Contact;
16 | import com.outlook.dev.service.OutlookService;
17 | import com.outlook.dev.service.OutlookServiceBuilder;
18 | import com.outlook.dev.service.PagedResult;
19 |
20 | @Controller
21 | public class ContactsController {
22 | @RequestMapping("/contacts")
23 | public String contacts(Model model, HttpServletRequest request, RedirectAttributes redirectAttributes) {
24 | HttpSession session = request.getSession();
25 | TokenResponse tokens = (TokenResponse)session.getAttribute("tokens");
26 | if (tokens == null) {
27 | // No tokens in session, user needs to sign in
28 | redirectAttributes.addFlashAttribute("error", "Please sign in to continue.");
29 | return "redirect:/index.html";
30 | }
31 |
32 | Date now = new Date();
33 | if (now.after(tokens.getExpirationTime())) {
34 | // Token expired
35 | // TODO: Use the refresh token to request a new token from the token endpoint
36 | // For now, just complain
37 | redirectAttributes.addFlashAttribute("error", "The access token has expired. Please logout and re-login.");
38 | return "redirect:/index.html";
39 | }
40 |
41 | String email = (String)session.getAttribute("userEmail");
42 |
43 | OutlookService outlookService = OutlookServiceBuilder.getOutlookService(tokens.getAccessToken(), email);
44 |
45 | // Sort by given name in ascending order (A-Z)
46 | String sort = "givenName ASC";
47 | // Only return the properties we care about
48 | String properties = "givenName,surname,companyName,emailAddresses";
49 | // Return at most 10 contacts
50 | Integer maxResults = 10;
51 |
52 | try {
53 | PagedResult contacts = outlookService.getContacts(
54 | sort, properties, maxResults)
55 | .execute().body();
56 | model.addAttribute("contacts", contacts.getValue());
57 | } catch (IOException e) {
58 | redirectAttributes.addFlashAttribute("error", e.getMessage());
59 | return "redirect:/index.html";
60 | }
61 |
62 | return "contacts";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/auth/TokenResponse.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.auth;
2 |
3 | import java.util.Calendar;
4 | import java.util.Date;
5 |
6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
7 | import com.fasterxml.jackson.annotation.JsonProperty;
8 |
9 | @JsonIgnoreProperties(ignoreUnknown = true)
10 | public class TokenResponse {
11 | @JsonProperty("token_type")
12 | private String tokenType;
13 | private String scope;
14 | @JsonProperty("expires_in")
15 | private int expiresIn;
16 | @JsonProperty("access_token")
17 | private String accessToken;
18 | @JsonProperty("refresh_token")
19 | private String refreshToken;
20 | @JsonProperty("id_token")
21 | private String idToken;
22 | private String error;
23 | @JsonProperty("error_description")
24 | private String errorDescription;
25 | @JsonProperty("error_codes")
26 | private int[] errorCodes;
27 | private Date expirationTime;
28 |
29 | public String getTokenType() {
30 | return tokenType;
31 | }
32 | public void setTokenType(String tokenType) {
33 | this.tokenType = tokenType;
34 | }
35 | public String getScope() {
36 | return scope;
37 | }
38 | public void setScope(String scope) {
39 | this.scope = scope;
40 | }
41 | public int getExpiresIn() {
42 | return expiresIn;
43 | }
44 | public void setExpiresIn(int expiresIn) {
45 | this.expiresIn = expiresIn;
46 | Calendar now = Calendar.getInstance();
47 | now.add(Calendar.SECOND, expiresIn);
48 | this.expirationTime = now.getTime();
49 | }
50 | public String getAccessToken() {
51 | return accessToken;
52 | }
53 | public void setAccessToken(String accessToken) {
54 | this.accessToken = accessToken;
55 | }
56 | public String getRefreshToken() {
57 | return refreshToken;
58 | }
59 | public void setRefreshToken(String refreshToken) {
60 | this.refreshToken = refreshToken;
61 | }
62 | public String getIdToken() {
63 | return idToken;
64 | }
65 | public void setIdToken(String idToken) {
66 | this.idToken = idToken;
67 | }
68 | public String getError() {
69 | return error;
70 | }
71 | public void setError(String error) {
72 | this.error = error;
73 | }
74 | public String getErrorDescription() {
75 | return errorDescription;
76 | }
77 | public void setErrorDescription(String errorDescription) {
78 | this.errorDescription = errorDescription;
79 | }
80 | public int[] getErrorCodes() {
81 | return errorCodes;
82 | }
83 | public void setErrorCodes(int[] errorCodes) {
84 | this.errorCodes = errorCodes;
85 | }
86 | public Date getExpirationTime() {
87 | return expirationTime;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/layout/base.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2 | <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
3 | <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
4 | <%@ taglib uri="http://tiles.apache.org/tags-tiles-extras" prefix="tilesx" %>
5 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with the Microsoft Graph Outlook Mail API and Java
2 |
3 | The sample code in this repository is the end result of going through the [Java tutorial on the Outlook Dev Center](https://docs.microsoft.com/en-us/outlook/rest/java-tutorial). If you go through that tutorial yourself, you should end up with code very similar to this. If you download or fork this repository, you'll need to follow the steps in [Configure the sample](#configure-the-sample) to run it.
4 |
5 | > **NOTE:** Looking for the version of this tutorial that used the Outlook API directly instead of Microsoft Graph? Check out the `outlook-api` branch. Note that Microsoft recommends using the Microsoft Graph to access mail, calendar, and contacts. You should use the Outlook APIs directly (via https://outlook.office.com/api) only if you require a feature that is not available on the Graph endpoints.
6 |
7 | ## Prerequisites
8 |
9 | - Java Development Kit 8.
10 | - Spring Tool Suite
11 |
12 | ## Register the app
13 |
14 | Head over to https://apps.dev.microsoft.com to quickly get a application ID and password. Click the **Sign in** link and sign in with either your Microsoft account (Outlook.com), or your work or school account (Office 365).
15 |
16 | Once you're signed in, click the **Add an app** button. Enter `java-tutorial` for the name and click **Create application**. After the app is created, locate the **Application Secrets** section, and click the **Generate New Password** button. Copy the password now and save it to a safe place. Once you've copied the password, click **Ok**.
17 |
18 | 
19 |
20 | Locate the **Platforms** section, and click **Add Platform**. Choose **Web**, then enter `http://localhost:8080/authorize.html` under **Redirect URIs**.
21 |
22 | > **NOTE:** The values in **Redirect URIs** are case-sensitive, so be sure to match the case!
23 |
24 | Click **Save** to complete the registration. Copy the **Application Id** and save it along with the password you copied earlier. We'll need those values soon.
25 |
26 | Here's what the details of your app registration should look like when you are done.
27 |
28 | 
29 |
30 | ## Configure the sample
31 |
32 | 1. Open the `./src/main/resources/auth.properties` file.
33 | 1. Replace `YOUR_APP_ID_HERE` with the **Application Id** from the registration you just created.
34 | 1. Replace `YOUR_APP_PASSWORD_HERE` with the password you copied earlier.
35 | 1. If you use an IDE, Right-click the project and choose **Run as**, then **Maven build**.
36 | 1. From a console, run **mvn package** and **mvn jetty:run** to start up a server
37 | 1. Access `http://localhost:8080`
38 |
39 | ## Copyright ##
40 |
41 | Copyright (c) Microsoft. All rights reserved.
42 |
43 | ----------
44 | Connect with me on Twitter [@JasonJohMSFT](https://twitter.com/JasonJohMSFT)
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/controller/AuthorizeController.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.controller;
2 |
3 | import java.io.IOException;
4 | import java.util.UUID;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpSession;
8 |
9 | import org.springframework.stereotype.Controller;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RequestMethod;
12 | import org.springframework.web.bind.annotation.RequestParam;
13 |
14 | import com.outlook.dev.auth.AuthHelper;
15 | import com.outlook.dev.auth.IdToken;
16 | import com.outlook.dev.auth.TokenResponse;
17 | import com.outlook.dev.service.OutlookService;
18 | import com.outlook.dev.service.OutlookServiceBuilder;
19 | import com.outlook.dev.service.OutlookUser;
20 |
21 | @Controller
22 | public class AuthorizeController {
23 |
24 | @RequestMapping(value="/authorize", method=RequestMethod.POST)
25 | public String authorize(
26 | @RequestParam("code") String code,
27 | @RequestParam("id_token") String idToken,
28 | @RequestParam("state") UUID state,
29 | HttpServletRequest request) {
30 | // Get the expected state value from the session
31 | HttpSession session = request.getSession();
32 | UUID expectedState = (UUID) session.getAttribute("expected_state");
33 | UUID expectedNonce = (UUID) session.getAttribute("expected_nonce");
34 | session.removeAttribute("expected_state");
35 | session.removeAttribute("expected_nonce");
36 |
37 | // Make sure that the state query parameter returned matches
38 | // the expected state
39 | if (state.equals(expectedState)) {
40 | IdToken idTokenObj = IdToken.parseEncodedToken(idToken, expectedNonce.toString());
41 | if (idTokenObj != null) {
42 | TokenResponse tokenResponse = AuthHelper.getTokenFromAuthCode(code, idTokenObj.getTenantId());
43 | session.setAttribute("tokens", tokenResponse);
44 | session.setAttribute("userConnected", true);
45 | session.setAttribute("userName", idTokenObj.getName());
46 | session.setAttribute("userTenantId", idTokenObj.getTenantId());
47 | // Get user info
48 | OutlookService outlookService = OutlookServiceBuilder.getOutlookService(tokenResponse.getAccessToken(), null);
49 | OutlookUser user;
50 | try {
51 | user = outlookService.getCurrentUser().execute().body();
52 | session.setAttribute("userEmail", user.getMail());
53 | } catch (IOException e) {
54 | session.setAttribute("error", e.getMessage());
55 | }
56 | } else {
57 | session.setAttribute("error", "ID token failed validation.");
58 | }
59 | } else {
60 | session.setAttribute("error", "Unexpected state returned from authority.");
61 | }
62 | return "redirect:/mail.html";
63 | }
64 |
65 | @RequestMapping("/logout")
66 | public String logout(HttpServletRequest request) {
67 | HttpSession session = request.getSession();
68 | session.invalidate();
69 | return "redirect:/index.html";
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/auth/IdToken.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.auth;
2 |
3 | import java.util.Base64;
4 | import java.util.Date;
5 |
6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
7 | import com.fasterxml.jackson.annotation.JsonProperty;
8 | import com.fasterxml.jackson.databind.ObjectMapper;
9 |
10 | @JsonIgnoreProperties(ignoreUnknown = true)
11 | public class IdToken {
12 | // NOTE: This is just a subset of the claims returned in the
13 | // ID token. For a full listing, see:
14 | // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-tokens/#idtokens
15 | @JsonProperty("exp")
16 | private long expirationTime;
17 | @JsonProperty("nbf")
18 | private long notBefore;
19 | @JsonProperty("tid")
20 | private String tenantId;
21 | private String nonce;
22 | private String name;
23 | private String email;
24 | @JsonProperty("preferred_username")
25 | private String preferredUsername;
26 | @JsonProperty("oid")
27 | private String objectId;
28 |
29 | public static IdToken parseEncodedToken(String encodedToken, String nonce) {
30 | // Encoded token is in three parts, separated by '.'
31 | String[] tokenParts = encodedToken.split("\\.");
32 |
33 | // The three parts are: header.token.signature
34 | String idToken = tokenParts[1];
35 |
36 | byte[] decodedBytes = Base64.getUrlDecoder().decode(idToken);
37 |
38 | ObjectMapper mapper = new ObjectMapper();
39 | IdToken newToken = null;
40 | try {
41 | newToken = mapper.readValue(decodedBytes, IdToken.class);
42 | if (!newToken.isValid(nonce)) {
43 | return null;
44 | }
45 | } catch (Exception e) {
46 | e.printStackTrace();
47 | }
48 | return newToken;
49 | }
50 |
51 | public long getExpirationTime() {
52 | return expirationTime;
53 | }
54 |
55 | public void setExpirationTime(long expirationTime) {
56 | this.expirationTime = expirationTime;
57 | }
58 |
59 | public long getNotBefore() {
60 | return notBefore;
61 | }
62 |
63 | public void setNotBefore(long notBefore) {
64 | this.notBefore = notBefore;
65 | }
66 |
67 | public String getTenantId() {
68 | return tenantId;
69 | }
70 |
71 | public void setTenantId(String tenantId) {
72 | this.tenantId = tenantId;
73 | }
74 |
75 | public String getNonce() {
76 | return nonce;
77 | }
78 |
79 | public void setNonce(String nonce) {
80 | this.nonce = nonce;
81 | }
82 |
83 | public String getName() {
84 | return name;
85 | }
86 |
87 | public void setName(String name) {
88 | this.name = name;
89 | }
90 |
91 | public String getEmail() {
92 | return email;
93 | }
94 |
95 | public void setEmail(String email) {
96 | this.email = email;
97 | }
98 |
99 | public String getPreferredUsername() {
100 | return preferredUsername;
101 | }
102 |
103 | public void setPreferredUsername(String preferredUsername) {
104 | this.preferredUsername = preferredUsername;
105 | }
106 |
107 | public String getObjectId() {
108 | return objectId;
109 | }
110 |
111 | public void setObjectId(String objectId) {
112 | this.objectId = objectId;
113 | }
114 |
115 | private Date getUnixEpochAsDate(long epoch) {
116 | // Epoch timestamps are in seconds,
117 | // but Jackson converts integers as milliseconds.
118 | // Rather than create a custom deserializer, this helper will do
119 | // the conversion.
120 | return new Date(epoch * 1000);
121 | }
122 |
123 | private boolean isValid(String nonce) {
124 | // This method does some basic validation
125 | // For more information on validation of ID tokens, see
126 | // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-tokens/#validating-tokens
127 | Date now = new Date();
128 |
129 | // Check expiration and not before times
130 | if (now.after(this.getUnixEpochAsDate(this.expirationTime)) ||
131 | now.before(this.getUnixEpochAsDate(this.notBefore))) {
132 | // Token is not within it's valid "time"
133 | return false;
134 | }
135 |
136 | // Check nonce
137 | if (!nonce.equals(this.getNonce())) {
138 | // Nonce mismatch
139 | return false;
140 | }
141 |
142 | return true;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.outlook.dev
5 | java-tutorial
6 | 0.0.1-SNAPSHOT
7 | war
8 |
9 |
10 |
11 | org.eclipse.jetty
12 | jetty-maven-plugin
13 | 9.3.8.v20160314
14 |
15 |
16 | org.apache.maven.plugins
17 | maven-compiler-plugin
18 | 3.5.1
19 |
20 | 1.8
21 | 1.8
22 |
23 |
24 |
25 |
26 |
27 |
28 | 4.2.5.RELEASE
29 | 3.0.5
30 | 2.7.4
31 | 2.0.2
32 |
33 |
34 |
35 |
36 |
37 | org.springframework
38 | spring-framework-bom
39 | ${org.springframework.version}
40 | pom
41 | import
42 |
43 |
44 |
45 |
46 |
47 |
48 | javax.servlet
49 | javax.servlet-api
50 | 4.0.0-b01
51 | provided
52 |
53 |
54 |
55 | javax.servlet.jsp
56 | javax.servlet.jsp-api
57 | 2.3.2-b02
58 | provided
59 |
60 |
61 |
62 | jstl
63 | jstl
64 | 1.2
65 |
66 |
67 |
68 | org.springframework
69 | spring-core
70 |
71 |
72 |
73 | org.springframework
74 | spring-expression
75 |
76 |
77 |
78 | org.springframework
79 | spring-beans
80 |
81 |
82 |
83 | org.springframework
84 | spring-aop
85 |
86 |
87 |
88 | org.springframework
89 | spring-context
90 |
91 |
92 |
93 | org.springframework
94 | spring-context-support
95 |
96 |
97 |
98 | org.springframework
99 | spring-tx
100 |
101 |
102 |
103 | org.springframework
104 | spring-jdbc
105 |
106 |
107 |
108 | org.springframework
109 | spring-orm
110 |
111 |
112 |
113 | org.springframework
114 | spring-oxm
115 |
116 |
117 |
118 | org.springframework
119 | spring-web
120 |
121 |
122 |
123 | org.springframework
124 | spring-webmvc
125 |
126 |
127 |
128 | org.springframework
129 | spring-webmvc-portlet
130 |
131 |
132 |
133 | org.apache.tiles
134 | tiles-core
135 | ${apache.tiles.version}
136 |
137 |
138 |
139 | org.apache.tiles
140 | tiles-jsp
141 | ${apache.tiles.version}
142 |
143 |
144 |
145 | org.slf4j
146 | slf4j-log4j12
147 | 1.7.21
148 |
149 |
150 |
151 | com.fasterxml.jackson.core
152 | jackson-core
153 | ${jackson.version}
154 |
155 |
156 |
157 | com.fasterxml.jackson.core
158 | jackson-annotations
159 | ${jackson.version}
160 |
161 |
162 |
163 | com.fasterxml.jackson.core
164 | jackson-databind
165 | ${jackson.version}
166 |
167 |
168 |
169 | com.squareup.retrofit2
170 | retrofit
171 | ${retrofit.version}
172 |
173 |
174 |
175 | com.squareup.retrofit2
176 | converter-jackson
177 | ${retrofit.version}
178 |
179 |
180 |
181 | com.squareup.okhttp3
182 | logging-interceptor
183 | 3.2.0
184 |
185 |
186 |
--------------------------------------------------------------------------------
/src/main/java/com/outlook/dev/auth/AuthHelper.java:
--------------------------------------------------------------------------------
1 | package com.outlook.dev.auth;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.util.Calendar;
7 | import java.util.Properties;
8 | import java.util.UUID;
9 |
10 | import org.springframework.web.util.UriComponentsBuilder;
11 |
12 | import okhttp3.OkHttpClient;
13 | import okhttp3.logging.HttpLoggingInterceptor;
14 | import retrofit2.Retrofit;
15 | import retrofit2.converter.jackson.JacksonConverterFactory;
16 |
17 | public class AuthHelper {
18 | private static final String authority = "https://login.microsoftonline.com";
19 | private static final String authorizeUrl = authority + "/common/oauth2/v2.0/authorize";
20 |
21 | private static String[] scopes = {
22 | "openid",
23 | "offline_access",
24 | "profile",
25 | "User.Read",
26 | "Mail.Read",
27 | "Calendars.Read",
28 | "Contacts.Read"
29 | };
30 |
31 | private static String appId = null;
32 | private static String appPassword = null;
33 | private static String redirectUrl = null;
34 |
35 | private static String getAppId() {
36 | if (appId == null) {
37 | try {
38 | loadConfig();
39 | } catch (Exception e) {
40 | return null;
41 | }
42 | }
43 | return appId;
44 | }
45 | private static String getAppPassword() {
46 | if (appPassword == null) {
47 | try {
48 | loadConfig();
49 | } catch (Exception e) {
50 | return null;
51 | }
52 | }
53 | return appPassword;
54 | }
55 |
56 | private static String getRedirectUrl() {
57 | if (redirectUrl == null) {
58 | try {
59 | loadConfig();
60 | } catch (Exception e) {
61 | return null;
62 | }
63 | }
64 | return redirectUrl;
65 | }
66 |
67 | private static String getScopes() {
68 | StringBuilder sb = new StringBuilder();
69 | for (String scope: scopes) {
70 | sb.append(scope + " ");
71 | }
72 | return sb.toString().trim();
73 | }
74 |
75 | private static void loadConfig() throws IOException {
76 | String authConfigFile = "auth.properties";
77 | InputStream authConfigStream = AuthHelper.class.getClassLoader().getResourceAsStream(authConfigFile);
78 |
79 | if (authConfigStream != null) {
80 | Properties authProps = new Properties();
81 | try {
82 | authProps.load(authConfigStream);
83 | appId = authProps.getProperty("appId");
84 | appPassword = authProps.getProperty("appPassword");
85 | redirectUrl = authProps.getProperty("redirectUrl");
86 | } finally {
87 | authConfigStream.close();
88 | }
89 | }
90 | else {
91 | throw new FileNotFoundException("Property file '" + authConfigFile + "' not found in the classpath.");
92 | }
93 | }
94 |
95 | public static String getLoginUrl(UUID state, UUID nonce) {
96 |
97 | UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromHttpUrl(authorizeUrl);
98 | urlBuilder.queryParam("client_id", getAppId());
99 | urlBuilder.queryParam("redirect_uri", getRedirectUrl());
100 | urlBuilder.queryParam("response_type", "code id_token");
101 | urlBuilder.queryParam("scope", getScopes());
102 | urlBuilder.queryParam("state", state);
103 | urlBuilder.queryParam("nonce", nonce);
104 | urlBuilder.queryParam("response_mode", "form_post");
105 |
106 | return urlBuilder.toUriString();
107 | }
108 |
109 | public static TokenResponse getTokenFromAuthCode(String authCode, String tenantId) {
110 | // Create a logging interceptor to log request and responses
111 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
112 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
113 |
114 | OkHttpClient client = new OkHttpClient.Builder()
115 | .addInterceptor(interceptor).build();
116 |
117 | // Create and configure the Retrofit object
118 | Retrofit retrofit = new Retrofit.Builder()
119 | .baseUrl(authority)
120 | .client(client)
121 | .addConverterFactory(JacksonConverterFactory.create())
122 | .build();
123 |
124 | // Generate the token service
125 | TokenService tokenService = retrofit.create(TokenService.class);
126 |
127 | try {
128 | return tokenService.getAccessTokenFromAuthCode(tenantId, getAppId(), getAppPassword(),
129 | "authorization_code", authCode, getRedirectUrl()).execute().body();
130 | } catch (IOException e) {
131 | TokenResponse error = new TokenResponse();
132 | error.setError("IOException");
133 | error.setErrorDescription(e.getMessage());
134 | return error;
135 | }
136 | }
137 |
138 | public static TokenResponse ensureTokens(TokenResponse tokens, String tenantId) {
139 | // Are tokens still valid?
140 | Calendar now = Calendar.getInstance();
141 | if (now.getTime().after(tokens.getExpirationTime())) {
142 | // Still valid, return them as-is
143 | return tokens;
144 | }
145 | else {
146 | // Expired, refresh the tokens
147 | // Create a logging interceptor to log request and responses
148 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
149 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
150 |
151 | OkHttpClient client = new OkHttpClient.Builder()
152 | .addInterceptor(interceptor).build();
153 |
154 | // Create and configure the Retrofit object
155 | Retrofit retrofit = new Retrofit.Builder()
156 | .baseUrl(authority)
157 | .client(client)
158 | .addConverterFactory(JacksonConverterFactory.create())
159 | .build();
160 |
161 | // Generate the token service
162 | TokenService tokenService = retrofit.create(TokenService.class);
163 |
164 | try {
165 | return tokenService.getAccessTokenFromRefreshToken(tenantId, getAppId(), getAppPassword(),
166 | "refresh_token", tokens.getRefreshToken(), getRedirectUrl()).execute().body();
167 | } catch (IOException e) {
168 | TokenResponse error = new TokenResponse();
169 | error.setError("IOException");
170 | error.setErrorDescription(e.getMessage());
171 | return error;
172 | }
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------