Skip to content

Commit 433dcc4

Browse files
authored
Merge pull request #537 from simply-alliv/social-routes
feat: implement social routes
2 parents ea09b9b + be554a9 commit 433dcc4

25 files changed

+1494
-82
lines changed

package-lock.json

Lines changed: 317 additions & 73 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,20 @@
3030
},
3131
"homepage": "https://github.com/microapidev/auth-microapi#readme",
3232
"dependencies": {
33+
"axios": "^0.19.2",
3334
"bcrypt": "^5.0.0",
3435
"bcryptjs": "^2.4.3",
3536
"cors": "^2.8.5",
3637
"cross-env": "^7.0.2",
3738
"debug": "^4.1.1",
3839
"dotenv": "^8.2.0",
3940
"express": "^4.17.1",
40-
"express-jwt": "^6.0.0",
41+
"express-jwt": "^5.3.3",
4142
"express-validator": "^6.6.1",
43+
"googleapis": "^58.0.0",
4244
"jsonwebtoken": "^8.5.1",
4345
"mongoose": "^5.9.27",
46+
"oauth": "^0.9.15",
4447
"swagger-jsdoc": "^4.0.0",
4548
"swagger-ui-express": "^4.1.4"
4649
},

src/controllers/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as socialControllers from "./socialControllers";
2+
3+
export { socialControllers };
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import url from "url";
2+
import { InternalServerError } from "../utils/customError";
3+
import responseHandler from "../utils/responseHandler";
4+
import {
5+
FacebookService,
6+
GitHubService,
7+
GoogleService,
8+
TwitterService,
9+
} from "../services";
10+
11+
/**
12+
* Controller responsible for returning an authentication URL for the client
13+
* to initiate the Facebook's authentication flow.
14+
*/
15+
export const facebookController = (req, res, next) => {
16+
try {
17+
const facebookService = new FacebookService();
18+
facebookService.authenticate(req.provider, req.socialCallback);
19+
const authenticationUrl = facebookService.getAuthenticationUrl();
20+
21+
responseHandler(res, 200, { authenticationUrl });
22+
} catch (error) {
23+
next(new InternalServerError(error));
24+
}
25+
};
26+
27+
/**
28+
* Controller responsible for returning an authentication URL for the client
29+
* to initiate the GitHub's authentication flow.
30+
*/
31+
export const githubController = (req, res, next) => {
32+
try {
33+
const githubService = new GitHubService();
34+
githubService.authenticate(req.provider, req.socialCallback);
35+
const authenticationUrl = githubService.getAuthenticationUrl();
36+
37+
responseHandler(res, 200, { authenticationUrl });
38+
} catch (error) {
39+
next(new InternalServerError(error));
40+
}
41+
};
42+
43+
/**
44+
* Controller responsible for returning an authentication URL for the client
45+
* to initiate the Google's authentication flow.
46+
*/
47+
export const googleController = (req, res, next) => {
48+
try {
49+
const googleService = new GoogleService();
50+
googleService.authenticate(req.provider, req.socialCallback);
51+
const authenticationUrl = googleService.getAuthenticationUrl();
52+
53+
responseHandler(res, 200, { authenticationUrl });
54+
} catch (error) {
55+
next(new InternalServerError(error));
56+
}
57+
};
58+
59+
/**
60+
* Controller responsible for returning an authentication URL for the client
61+
* to initiate the Twitter's authentication flow.
62+
*/
63+
export const twitterController = async (req, res, next) => {
64+
try {
65+
const twitterService = new TwitterService();
66+
twitterService.authenticate(req.provider, req.socialCallback);
67+
const authenticationUrl = await twitterService.getAuthenticationUrl();
68+
69+
responseHandler(res, 200, { authenticationUrl });
70+
} catch (error) {
71+
next(new InternalServerError(error));
72+
}
73+
};
74+
75+
/**
76+
* Controller responsible for handling the callback from Facebook's authentication flow and
77+
* returning the user data to the client via a redirect to the client's provided
78+
* socialCallback.
79+
*/
80+
export const facebookCallbackController = async (req, res, next) => {
81+
try {
82+
// The state contains the client ID.
83+
const { state, code } = req.query;
84+
const facebookService = new FacebookService();
85+
const { profile, socialCallback } = await facebookService.getData(
86+
state.toString(),
87+
code
88+
);
89+
90+
let redirectUrl;
91+
92+
if (profile && profile.facebookId) {
93+
redirectUrl = url.format({
94+
pathname: socialCallback,
95+
query: {
96+
success: true,
97+
id: profile.facebookId,
98+
isVerified: profile.isVerified,
99+
firstname: profile.firstname,
100+
lastname: profile.lastname,
101+
username: profile.username,
102+
email: profile.email,
103+
photo: profile.photo,
104+
},
105+
});
106+
} else {
107+
redirectUrl = url.format({
108+
pathname: socialCallback,
109+
query: { success: false },
110+
});
111+
}
112+
113+
res.redirect(redirectUrl);
114+
} catch (error) {
115+
next(new InternalServerError(error));
116+
}
117+
};
118+
119+
/**
120+
* Controller responsible for handling the callback from GitHub's authentication flow and
121+
* returning the user data to the client via a redirect to the client's provided
122+
* socialCallback.
123+
*/
124+
export const githubCallbackController = async (req, res, next) => {
125+
try {
126+
// The state contains the client ID.
127+
const { state, code } = req.query;
128+
const githubService = new GitHubService();
129+
const { profile, socialCallback } = await githubService.getData(
130+
state.toString(),
131+
code
132+
);
133+
134+
let redirectUrl;
135+
136+
if (profile && profile.githubId) {
137+
redirectUrl = url.format({
138+
pathname: socialCallback,
139+
query: {
140+
success: true,
141+
id: profile.githubId,
142+
isVerified: profile.isVerified,
143+
firstname: profile.firstname,
144+
lastname: profile.lastname,
145+
username: profile.username,
146+
email: profile.email,
147+
photo: profile.photo,
148+
},
149+
});
150+
} else {
151+
redirectUrl = url.format({
152+
pathname: socialCallback,
153+
query: { success: false },
154+
});
155+
}
156+
157+
res.redirect(redirectUrl);
158+
} catch (error) {
159+
next(new InternalServerError(error));
160+
}
161+
};
162+
163+
/**
164+
* Controller responsible for handling the callback from Google's authentication flow and
165+
* returning the user data to the client via a redirect to the client's provided
166+
* socialCallback.
167+
*/
168+
export const googleCallbackController = async (req, res, next) => {
169+
try {
170+
// The state contains the client ID.
171+
const { state, code } = req.query;
172+
173+
const googleService = new GoogleService();
174+
const { profile, socialCallback } = await googleService.getData(
175+
state.toString(),
176+
code
177+
);
178+
179+
let redirectUrl;
180+
181+
if (profile && profile.googleId) {
182+
redirectUrl = url.format({
183+
pathname: socialCallback,
184+
query: {
185+
success: true,
186+
id: profile.googleId,
187+
isVerified: profile.isVerified,
188+
firstname: profile.firstname,
189+
lastname: profile.lastname,
190+
username: profile.username,
191+
email: profile.email,
192+
photo: profile.photo,
193+
},
194+
});
195+
} else {
196+
redirectUrl = url.format({
197+
pathname: socialCallback,
198+
query: { success: false },
199+
});
200+
}
201+
202+
res.redirect(redirectUrl);
203+
} catch (error) {
204+
next(new InternalServerError(error));
205+
}
206+
};
207+
208+
/**
209+
* Controller responsible for handling the callback from Twitter's authentication flow and
210+
* returning the user data to the client via a redirect to the client's provided
211+
* socialCallback.
212+
*/
213+
export const twitterCallbackController = async (req, res, next) => {
214+
try {
215+
// The state contains the client ID.
216+
const { oauth_token, oauth_verifier } = req.query;
217+
218+
const twitterService = new TwitterService();
219+
const { profile, socialCallback } = await twitterService.getData(
220+
oauth_token.toString(),
221+
oauth_verifier.toString()
222+
);
223+
224+
let redirectUrl;
225+
226+
if (profile && profile.twitterId) {
227+
redirectUrl = url.format({
228+
pathname: socialCallback,
229+
query: {
230+
success: true,
231+
id: profile.twitterId,
232+
isVerified: profile.isVerified,
233+
firstname: profile.firstname,
234+
lastname: profile.lastname,
235+
username: profile.username,
236+
email: profile.email,
237+
photo: profile.photo,
238+
},
239+
});
240+
} else {
241+
redirectUrl = url.format({
242+
pathname: socialCallback,
243+
query: { success: false },
244+
});
245+
}
246+
247+
res.redirect(redirectUrl);
248+
} catch (error) {
249+
next(new InternalServerError(error));
250+
}
251+
};

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import express from "express";
22
import cors from "cors";
33
import CustomError from "./utils/customError";
44
import errorHandler from "./utils/errorhandler";
5-
import { docRouter } from "./routes";
5+
import { docRouter, socialRouter } from "./routes";
66

77
// create express app
88
const app = express();
@@ -16,6 +16,7 @@ app.use(express.urlencoded({ extended: true }));
1616

1717
// docuementation routes
1818
app.use("/", docRouter);
19+
app.use("/social", socialRouter);
1920

2021
// routes not found go here
2122
app.all("*", (req, res, next) => {

src/jsdocs/providers/facebook.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
paths:
2+
# Endpoint to authenticate a Facebook user
3+
/social/facebook:
4+
# GET operation
5+
get:
6+
tags:
7+
- providers
8+
9+
summary: Authenticates with Facebook
10+
11+
responses:
12+
"200":
13+
description: Successful operation
14+
schema:
15+
type: object
16+
properties:
17+
status:
18+
type: string
19+
example: success
20+
data:
21+
type: object
22+
properties:
23+
authenticationUrl:
24+
type: string
25+
example: https://www.facebook.com/v4.0/dialog/oauth
26+
"400":
27+
description: Invalid body supplied
28+
schema:
29+
$ref: "#/definitions/BadRequest"

src/jsdocs/providers/github.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
paths:
2+
# Endpoint to authenticate a GitHub user
3+
/social/github:
4+
# GET operation
5+
get:
6+
tags:
7+
- providers
8+
9+
summary: Authenticates with GitHub
10+
11+
responses:
12+
"200":
13+
description: Successful operation
14+
schema:
15+
type: object
16+
properties:
17+
status:
18+
type: string
19+
example: success
20+
data:
21+
type: object
22+
properties:
23+
authenticationUrl:
24+
type: string
25+
example: https://github.com/login/oauth/authorize
26+
"400":
27+
description: Invalid body supplied
28+
schema:
29+
$ref: "#/definitions/BadRequest"

src/jsdocs/providers/google.yaml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
paths:
2-
# Endpoint to authenticate a user
3-
/google:
2+
# Endpoint to authenticate a Google user
3+
/social/google:
44
# GET operation
55
get:
66
tags:
77
- providers
88

99
summary: Authenticates with Google
1010

11-
security:
12-
- AdminToken: []
13-
1411
responses:
1512
"200":
1613
description: Successful operation
@@ -23,7 +20,7 @@ paths:
2320
data:
2421
type: object
2522
properties:
26-
redirectUrl:
23+
authenticationUrl:
2724
type: string
2825
example: https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount
2926
"400":

0 commit comments

Comments
 (0)