All files / src/handlers RPInitiatedLogoutRequest.js

90% Statements 18/20
75% Branches 3/4
100% Functions 6/6
90% Lines 18/20
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155            1x 1x                               1x 1x   1x                                                     4x 4x                                                                                                     3x 3x 1x   2x                           1x   1x         1x                       2x   2x         2x       1x  
'use strict'
 
/**
 * Dependencies
 * @ignore
 */
const qs = require('qs')
const BaseRequest = require('./BaseRequest')
 
class RPInitiatedLogoutRequest extends BaseRequest {
  /**
   * RP Initiated Logout Request Handler
   *
   * @see https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
   * @see https://openid.net/specs/openid-connect-frontchannel-1_0.html#RPInitiated
   * @see https://openid.net/specs/openid-connect-backchannel-1_0.html#RPInitiated
   *
   * @param req {HTTPRequest}
   * @param res {HTTPResponse}
   * @param provider {Provider}
   * @returns {Promise}
   */
  static handle (req, res, provider) {
    let { host } = provider
    let request = new RPInitiatedLogoutRequest(req, res, provider)
 
    return Promise
      .resolve(request)
      .then(request.validate)
 
      // From: https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
      // At the logout endpoint, the OP SHOULD ask the End-User whether they want
      // to log out of the OP as well. If the End-User says "yes", then the OP
      // MUST log out the End-User.
      .then(host.logout)
 
      .then(request.redirectOrRespond.bind(request))
      .catch(request.error.bind(request))
  }
 
  /**
   * Constructor
   *
   * Session spec defines the following params to the RP Initiated Logout request:
   *   - `id_token_hint`
   *   - `post_logout_redirect_uri`
   *   - `state`
   *
   * @param req {HTTPRequest}
   * @param res {HTTPResponse}
   * @param provider {Provider}
   */
  constructor (req, res, provider) {
    super(req, res, provider)
    this.params = RPInitiatedLogoutRequest.getParams(this)
  }
 
  /**
   * Validate
   *
   * @see https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
   *
   * @param request {RPInitiatedLogoutRequest}
   */
  validate (request) {
    /**
     * `state` parameter - no validation need it. Will be passed back to the RP
     * in the `redirectToRP()` step.
     */
 
    /**
     * TODO: Validate `id_token_hint` (validate as ID Token *except* for `aud`)
     *
     * RECOMMENDED. Previously issued ID Token passed to the logout endpoint as
     * a hint about the End-User's current authenticated session with the Client.
     * This is used as an indication of the identity of the End-User that the RP
     * is requesting be logged out by the OP. The OP *need not* be listed as an
     * audience of the ID Token when it is used as an `id_token_hint` value.
     */
 
    /**
     * TODO: Validate that `post_logout_redirect_uri` has been registered
     *
     * The value MUST have been previously registered with the OP, either using
     * the `post_logout_redirect_uris` Registration parameter
     * or via another mechanism.
     *
     * Question: what's this about 'another mechanism'?
     */
  }
 
  /**
   * Redirect to RP or Respond
   *
   * In some cases, the RP will request that the End-User's User Agent to be
   * redirected back to the RP after a logout has been performed. Post-logout
   * redirection is only done when the logout is RP-initiated, in which case the
   * redirection target is the `post_logout_redirect_uri` query parameter value
   * used by the initiating RP; otherwise it is not done.
   *
   * @see https://openid.net/specs/openid-connect-session-1_0.html#RedirectionAfterLogout
   *
   * @returns {null}
   */
  redirectOrRespond () {
    let { params: { post_logout_redirect_uri: postLogoutRedirectUri } } = this
    if (postLogoutRedirectUri) {
      this.redirectToRP()
    } else {
      this.respond()
    }
  }
 
  /**
   * Redirect To RP
   *
   * Redirects the user-agent back to the RP, if requested (by the RP providing
   * a `post_logout_redirect_uri` parameter). Also passes through the `state`
   * parameter, if supplied by the RP.
   *
   * @returns {null}
   */
  redirectToRP () {
    let { res, params: { post_logout_redirect_uri: uri, state } } = this
 
    Iif (state) {
      let response = qs.stringify({ state })
      uri = `${uri}?${response}`
    }
 
    res.redirect(uri)  // 302 redirect
  }
 
  /**
   * Respond
   *
   * Responds to the RP Initiated Logout request with a 204 No Content, if the
   * RP did not supply a `post_logout_redirect_uri` parameter.
   *
   * @returns {null}
   */
  respond () {
    let { res } = this
 
    res.set({
      'Cache-Control': 'no-store',
      'Pragma': 'no-cache'
    })
 
    res.status(204).send()
  }
}
 
module.exports = RPInitiatedLogoutRequest