openemail.security
Cada método de este espacio de nombres: su firma, sus parámetros, lo que devuelve y un ejemplo.
Métodos
Verification codes for apps connected with OAuth: check whether sensitive changes are unlocked, ask for a code and verify it. API keys are never asked for one.
security.stepUpStatus()
Check whether an OAuth token is verified for sensitive changes
stepUpStatus(options?: RequestScope): Promise<StepUpStatusResource>Tells an app connected with OAuth whether it may make sensitive changes right now. The web app asks a person for a verification code before a change that could lock them out or leak mail, and an app acting for that person is asked the same thing. An API key is never asked, so this call is for OAuth access tokens only.
elevated is true while a code verified through verifyStepUp still holds, and elevatedUntil says when that ends, or is null when there is nothing to end. The elevation belongs to this app acting for this person, and it holds for the app's MCP calls as well as for REST: the MCP tools that make the same guarded changes, such as createRule and removeDomain, run while it lasts. Another app they connected has to verify on its own, and signing in to the web app does not count here. The person can also allow this app from the website, with Allow changes for 60 minutes on the app in Account settings, Connected apps, and elevated is then true here as well.
method is how the next code would be checked. It is totp when the person has two-factor sign-in turned on, and then the code comes from their authenticator app, or is one of their backup codes. Otherwise it is email, and beginStepUp emails a code to the address they sign in with. minutes is how long an elevation lasts once a code is verified, currently 60.
Call it before a batch of sensitive changes to ask for a code once, up front, rather than waiting for the first 403 step_up_required.
Parámetros
options.signalAbortSignalCancels the request.
options.apiKeystringSends this API key instead of the client's credential for this call only. A key is answered 400
step_up_not_applicable.
Devuelve
StepUpStatusResource with object set to step_up, elevated, elevatedUntil, method and minutes.
Ejemplo
const status = await openemail.security.stepUpStatus() if (!status.elevated) { console.log(`Changes need a ${status.method === 'totp' ? 'code from your authenticator app' : 'code from your email'}`)}Notas
It needs no scope. Any access token the server accepts may ask about itself.
An API key is refused with 400
step_up_not_applicable. Keys are never asked for a code, so there is nothing to check.Reading the status changes nothing and sends nothing, so it is safe to call as often as you like.
It does not report a pause. After 10 wrong codes in 24 hours from this app, or 20 from all of the person's apps together,
beginStepUpandverifyStepUpanswer 429step_up_locked, and only their message says when verification resumes.
También disponible en
security.beginStepUp()
Ask for a verification code
beginStepUp(body?: StepUpBegin, options?: RequestScope): Promise<StepUpChallengeResource>Opens a verification challenge for an app connected with OAuth. Call it when a request fails with 403 step_up_required, or before a batch of sensitive changes, then pass the code the person gives you to verifyStepUp.
method says where the code comes from. With email a six digit code has just gone to the address the person signs in with, sentTo names that address with most of it hidden, such as a•••@example.com, and the code works for 10 minutes. The email says a connected app asked for the code and shows the name this app registered with, its client ID, the date the person connected it and, when the app was refused one in the last 15 minutes, the change it last asked to make, so they can see which app is asking before they hand the code over. It also tells them the code unlocks more than one change: for 60 minutes the app can make any change that needs a code and that they gave it access to. With totp nothing is sent: the person has two-factor sign-in turned on and reads the code from their authenticator app, or uses one of their backup codes.
A challenge that is still open and still has tries left is reused rather than replaced, so calling this twice does not send two emails. reused is true when that happened, and the code sent the first time is the one to enter. Pass resend: true to close the open challenge and start a new one, which emails a fresh code. A challenge that five wrong codes locked is never reused, and neither is one that expired, so after step_up_code_expired, or after the fifth wrong code, a plain beginStepUp() starts a fresh challenge without resend.
Each app has a budget of its own with each person: 5 new challenges an hour and 20 in 24 hours. Another app never uses it up, and neither does the person working in the web app, whose own budget is 10 codes an hour.
Wrong codes count against the app too. After 10 in 24 hours, across every challenge it opened, verification is paused for this app. After 20 in 24 hours from all the apps the person connected together, it is paused for every one of them, this app included, even if none of those codes came from it, and removing an app does not lift that pause. Codes entered in the web app count toward neither, and the person can still verify there. While a pause lasts, this call and verifyStepUp answer 429 step_up_locked with a message that says verification is paused until a given time, and no code is sent or checked before then.
An API key is never asked for a code, so this call is for OAuth access tokens only.
Parámetros
body.resendbooleanCloses the open challenge and starts a new one, emailing a fresh code when the method is
email. Leave it out to reuse a challenge that is still open and still has tries left. A locked or expired challenge is replaced either way, and every new challenge counts toward the app's 5 an hour and 20 in 24 hours.options.signalAbortSignalCancels the request.
options.apiKeystringSends this API key instead of the client's credential for this call only. A key is answered 400
step_up_not_applicable.
Devuelve
StepUpChallengeResource with object set to step_up_challenge, method, reused, and sentTo, the masked address, when the code went out by email.
Ejemplo
const challenge = await openemail.security.beginStepUp() console.log(challenge.method === 'email' ? `We emailed a code to ${challenge.sentTo}.` : 'Enter the code from your authenticator app, or a backup code.') const again = await openemail.security.beginStepUp({ resend: true }) console.log(again.reused)Notas
It needs no scope. An API key is refused with 400
step_up_not_applicable.An app can open 5 challenges an hour and 20 in 24 hours for each person. The next is refused with 429
step_up_throttled, and the message says when to try again. A reused challenge does not count, and neither does one whose email could not be delivered. Other apps have budgets of their own, and so does the web app, which allows 10 an hour.After 10 wrong codes in 24 hours this app is paused, and after 20 from all of the person's apps together every app is: 429
step_up_locked, with a message that says verification is paused until a given time.resend: truelifts neither pause. The person gets one email about each.When the email cannot be delivered the answer is 503
step_up_undeliverableand no challenge is left open. Try again in a moment.Not retried automatically, because a retry after a lost response could send a second email.
También disponible en
security.verifyStepUp()
Verify a code and unlock sensitive changes
verifyStepUp(body: StepUpVerify, options?: RequestScope): Promise<StepUpVerifiedResource>Checks the code the person entered against the challenge beginStepUp opened, and on a match lets this app make sensitive changes for them until elevatedUntil, 60 minutes from now. Replay the request that failed with 403 step_up_required once this resolves.
The verification belongs to this app acting for this person. It unlocks the 16 changes that ask for a code: webhooks.create, webhooks.update, rules.create, rules.update, roles.update, roles.delete, members.add, members.update, members.remove, members.grantAddress, members.revokeAddress, domains.delete, domains.deleteAddress, appHost.delete, audiences.delete and forms.delete. Each still needs the scope it always needs. It holds for the MCP tools that make the same changes as well (createRule, setRuleEnabled, removeDomain, removeDomainAddress, removeAppHost, deleteForm, and deleteAudience on any audience but the default one), which answer Refused (step_up_required) while the app is not verified. Nothing else asks for a code, audiences.empty and the emptyAudience tool included. Another app the person connected verifies on its own.
The code is the one emailed to them when the method is email, or a code from their authenticator app, or one of their backup codes, when it is totp. Spaces are ignored, so a code copied as 123 456 works. An authenticator code is accepted only once: entering one that was already accepted, by any app or in the web app, counts as a wrong code, so wait for the next one.
Open a challenge with beginStepUp first, even when the code comes from an authenticator app, because the code is checked against that challenge. A challenge allows five tries. The fifth wrong code locks it, and a locked challenge is never reused, so the next beginStepUp() starts a fresh one without resend.
Wrong codes also count against this app for 24 hours, across every challenge, and a correct code does not clear them. The tenth in 24 hours pauses verification for this app. They count toward a second limit as well, shared by every app the person connected: the twentieth wrong code in 24 hours from all of those apps together pauses verification for every one of them, this app included, even if none of those codes came from it. Removing an app from the person's connected apps does not lift that pause. Codes entered in the web app count toward neither limit, and the person can still verify there during either pause. While a pause lasts, this call and beginStepUp answer 429 step_up_locked with a message that says verification is paused until a given time, and until then even a correct code is refused.
An app that has no way to ask the person for a code, such as an MCP connector that only calls tools, does not need this call: the person can choose Allow changes for 60 minutes on the app in Account settings, Connected apps, on the website, which lets it make the same changes for 60 minutes. They can end that early from the same menu.
An API key is never asked for a code, so this call is for OAuth access tokens only.
Parámetros
body.codestringObligatorioThe code the person entered: six digits from the email or their authenticator app, or one of their backup codes. 6 to 12 characters once surrounding spaces are trimmed.
options.signalAbortSignalCancels the request.
options.apiKeystringSends this API key instead of the client's credential for this call only. A key is answered 400
step_up_not_applicable.
Devuelve
StepUpVerifiedResource with object set to step_up, elevated set to true and elevatedUntil, 60 minutes from now.
Ejemplo
import { OpenEmailApiError, openemail } from '@openemail/sdk' const update = () => openemail.webhooks.update('whe_3f9c2a7b1e4d8f60a5c7b92d', { enabled: false }) try { await update()} catch (error) { if (!(error instanceof OpenEmailApiError) || !error.isStepUpRequired) throw error await openemail.security.beginStepUp() const verified = await openemail.security.verifyStepUp({ code: await askForCode() }) console.log(verified.elevatedUntil) await update()}Notas
It needs no scope. An API key is refused with 400
step_up_not_applicable.A wrong code is 400
step_up_code_invalidoncode. The message says how many tries are left: the fewest of the tries left on this challenge, the wrong codes left before this app is paused and the wrong codes left before every app is paused.The fifth wrong code on a challenge, and every code sent after it, is 429
step_up_locked. Ask for a new code withbeginStepUp(), which starts a fresh challenge withoutresend.The tenth wrong code from this app in 24 hours is 429
step_up_lockedas well, and so is the twentieth from all of the person's apps together, and every call while either pause lasts. The message says verification is paused until a given time, which is how to tell a pause from a locked challenge, and it reads the same for both pauses. The person gets one email about each pause. The one for this app names it. The one for every app says codes from all the apps they connected are paused, names the app the last wrong code came from, and says codes entered on the website still work.An expired code, or no open challenge at all, is 400
step_up_code_expired. Ask for a new code withbeginStepUp().A code shorter than 6 or longer than 12 characters, or a body with any field besides
code, is refused before it is checked and does not use up a try.Not retried automatically. A retry after a lost response would spend a second attempt on a challenge the first one may already have closed.