Skip to content

Commit 6ef6a21

Browse files
authored
Specify Account Management API #4409
ref DEV-1267
2 parents 1c8f14a + 5829801 commit 6ef6a21

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

docs/specs/account-management-api.md

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Account Management API
2+
3+
Account Management API is an API for account management on behalf of the end user. Account Management API requires a authenticated session, either by cookie or the HTTP header Authorization.
4+
5+
Account Management API supports the following operations:
6+
7+
- [Manage identifications](#manage-identifications)
8+
- List identifications
9+
- Add a new email, with verification
10+
- Add a new phone number, with verification
11+
- Add a new username
12+
- [Start adding an OAuth provider account, with authorization code flow](#start-adding-an-oauth-provider-account-with-authorization-code-flow)
13+
- [Finish adding an OAuth provider account, with authorization code flow](#finish-adding-an-oauth-provider-account-with-authorization-code-flow)
14+
- Add a biometric
15+
- Add a passkey
16+
- Remove an email
17+
- Remove a phone number
18+
- Remove a username
19+
- Remove an OAuth provider account
20+
- Remove a biometric
21+
- Remove a passkey
22+
- Update an email, with verification
23+
- Update a phone number, with verification
24+
- Update a username
25+
- Manage authentications
26+
- List authentications, and recovery codes.
27+
- Change the primary password
28+
- Change the secondary password
29+
- Remove the secondary password
30+
- Add a new TOTP authenticator
31+
- Remove a TOTP authenticator
32+
- Add a new OOB-OTP authenticator, with verification.
33+
- Remove a OOB-OTP authenticator
34+
- Re-generate recovery codes
35+
- Manage user profile
36+
- Update simple standard attributes
37+
- Update custom attributes
38+
- Add user profile picture
39+
- Replace user profile picture
40+
- Remove user profile picture
41+
- Manage sessions
42+
- List sessions
43+
- Revoke a session
44+
- Terminate all other sessions
45+
- Auxiliary operations
46+
- Verify OTP
47+
- Resend OTP
48+
49+
## Manage identifications
50+
51+
### Start adding an OAuth provider account, with authorization code flow
52+
53+
> This endpoint is considered as advanced. It is assumed that you are capable of
54+
> receiving OAuth callback via `redirect_uri`.
55+
56+
`POST /api/v1/account/identification`
57+
58+
Request
59+
60+
```json
61+
{
62+
"identification": "oauth",
63+
"alias": "google",
64+
"redirect_uri": "https://myapp.authgear.cloud/sso/oauth2/callback/google",
65+
"exclude_state_in_authorization_url": true
66+
}
67+
```
68+
69+
- `identification`: Required. It must be the value `oauth`.
70+
- `alias`: Required. The alias of the OAuth provider you want the current account to associate with.
71+
- `redirect_uri`: Required. You have to specify your own redirect URI to your app or your website to receive the OAuth callback.
72+
- `exclude_state_in_authorization_url`: Optional. The default is false.
73+
- When it is false, the `authorization_url` has a `state` parameter included, the `token` is bound to this `state` parameter.
74+
- When is is true, the `authorization_url` has no `state` parameter included, the `token` is NOT bound to `state`.
75+
- If you wish to use your own state, you must specify `true` for this field.
76+
77+
Response
78+
79+
```json
80+
{
81+
"result": {
82+
"token": "oauthtoken_blahblahblah",
83+
"authorization_url": "https://www.google.com?client_id=client_id&redirect_uri=redirect_uri"
84+
}
85+
}
86+
```
87+
88+
- `token`: You store this token. You need to supply it after the end-user returns to your app.
89+
- `authorization_url`: You MUST redirect the end-user to this URL to continue the authorization code flow. If `exclude_state_in_authorization_url` is false, it has `state` parameter included.
90+
91+
Error response
92+
93+
|Description|Name|Reason|Info|
94+
|---|---|---|---|
95+
|If the request is not authenticated|Unauthorized|Unauthorized||
96+
97+
The OAuth provider ultimately will call your redirect URI with query parameters added. You continue the flow with [Finish adding an OAuth provider account, with authorization code flow](#finish-adding-an-oauth-provider-account-with-authorization-code-flow).
98+
99+
Here is some pseudo code that you should do
100+
101+
```javascript
102+
const response = fetch("https://myapp.authgear.cloud/api/v1/account/identification", {
103+
method: "POST",
104+
headers: {
105+
"Authorization": `Bearer ${ACCESS_TOKEN}`,
106+
"Content-Type": "application/json",
107+
},
108+
body: JSON.stringify({
109+
"identification": "oauth",
110+
"alias": "google",
111+
"redirect_uri": "com.myapp://host/path",
112+
"exclude_state_in_authorization_url": true,
113+
}),
114+
});
115+
const responseJSON = await response.json();
116+
// TODO: Add proper error handling here.
117+
// You cannot assume you always get result.
118+
const token = responseJSON.result.token;
119+
const authorizationURL = new URL(responseJSON.result.authorization_url);
120+
// Generate a random state that is NOT too long. The characters MUST be URL safe.
121+
const state = generateAlphaNumbericRandomStringOfLength(32);
122+
authorizationURL.searchParams.set("state", state);
123+
// Store the state in some persistent storage
124+
window.sessionStorage.setItem("resume", JSON.stringify({
125+
state,
126+
token,
127+
}));
128+
// Redirect to the OAuth provider.
129+
window.location.href = authorizationURL.toString();
130+
```
131+
132+
### Finish adding an OAuth provider account, with authorization code flow
133+
134+
`POST /api/v1/account/identification/oauth`
135+
136+
Request
137+
138+
```json
139+
{
140+
"token": "oauthtoken_blahblahblah",
141+
"query": "code=code"
142+
}
143+
```
144+
145+
- `token`: The `token` you received in the response of [Start adding an OAuth provider account, with authorization code flow](#start-adding-an-oauth-provider-account-with-authorization-code-flow).
146+
- `query`: The query of the redirect URI.
147+
148+
Response
149+
150+
If successful, then the OAuth provider account is added.
151+
152+
```json
153+
{
154+
"result": {}
155+
}
156+
```
157+
158+
Error response
159+
160+
|Description|Name|Reason|Info|
161+
|---|---|---|---|
162+
|If the request is not authenticated|Unauthorized|Unauthorized||
163+
|If the OAuth provider account is already taken by another account|Invalid|InvariantViolated|`{"cause": { "kind": "DuplicatedIdentity" } }`|
164+
|If `token` is invalid|Invalid|AccountManagementOAuthTokenInvalid||
165+
|If `exclude_state_in_authorization_url` is false, and `state` in `query` is not equal to the one bound to `token`|Invalid|AccountManagementOAuthStateNotBoundToToken||
166+
|If `token` is not bound to the current user|Invalid|AccountManagementOAuthTokenNotBoundToUser||
167+
168+
Here is some pseudo code that you should do
169+
170+
```javascript
171+
// The OAuth provider redirects the user back to us.
172+
const url = new URL(window.location.href);
173+
const state = url.searchParams.get("state");
174+
if (state == null) {
175+
// Expected state to be present.
176+
return;
177+
}
178+
const resumeStr = window.sessionStorage.getItem("resume");
179+
if (resumeStr == null) {
180+
// Expected resume to be non-null.
181+
return;
182+
}
183+
// Always remove resume.
184+
window.sessionStorage.removeItem("resume");
185+
186+
const resume = JSON.parse(resumeStr);
187+
if (state !== resume.state) {
188+
// Expected resume.state equals to state.
189+
return;
190+
}
191+
const token = resume.token;
192+
const response = fetch("https://myapp.authgear.cloud/api/v1/account/identification/oauth", {
193+
method: "POST",
194+
headers: {
195+
"Authorization": `Bearer ${ACCESS_TOKEN}`,
196+
"Content-Type": "application/json",
197+
},
198+
body: JSON.stringify({
199+
"token": token,
200+
"query": url.search,
201+
}),
202+
});
203+
const responseJSON = await response.json();
204+
// TODO: Add proper error handling here.
205+
```

0 commit comments

Comments
 (0)